/* Pekan OS 2031: the theme layer, round 6.
   Loaded after desktop.css and the app styles; it retunes the Win12 shell
   into the 2031 look: dark glass lit from within. One material language,
   three depths, dark is the default and the only mode. There is no light
   toggle and no dark toggle.

   Round 4 shipped light glass and the owner rejected bright. Round 6
   inverts the material to plan 4.1: lightness down, chroma up. Rich, not
   neon. Futuristic means depth and drift, not glow.

   Every value in the glass system is a custom property in this file. A
   component picks a depth and inherits; nothing hardcodes a surface.
   pekan/glass.js measures the wallpaper behind the glass and raises the
   dark surface alphas until light text over them clears 4.5:1, so the
   floor is measured rather than assumed.

   Brand tokens are the site's (00-Master-Plan/03-ui-vision.md) and are not
   invented here; the accent is the site purple deepened for dark ground.
   No em dashes anywhere in this file. */

/* Typography comes from the shared owner-supplied brand foundation. */
/* ===== tokens ===== */
:root {
  /* the site's brand tokens, pm- prefixed as the effects plugin exposes
     them. On dark ground the accent is the plan 4.1 deepened purple and
     the sun is the plan 4.1 amber, used sparingly. */
  --pm-purple: #6E5AE8;
  --pm-purple-deep: #8273FF;
  --pm-lavender: #A99BFF;
  --pm-sun: #E4C56A;
  --pm-sky: #4E93C8;
  --pm-teal: #2A6B6B;
  /* These four are the surface and ink names the plugin reads. They held a
     second literal copy of a value the glass ramp below already owns, with
     nothing keeping the two copies in step, so they are aliases now. Same
     computed value, one place to change it. A custom property may reference
     one declared later in the same rule: substitution happens at computed
     value time, not in source order. */
  --pm-ink: var(--g1-base);
  --pm-body: var(--os-text-2);
  --pm-surface: var(--g1-warm);
  --pm-border: var(--os-line-2);
  --pm-ease: cubic-bezier(.2,.7,.2,1);
  --pm-spring: cubic-bezier(.34,1.4,.5,1);

  /* =====================================================================
     ROUND 11: ONE RADIUS LADDER, ONE GLASS RECIPE.

     The owner's complaint was that the OS did not feel like one object.
     Measured on the build this replaces: five radii painted across the
     floating surfaces (12, 0, 14, 8 and 12 with a 10 in the source that
     never landed), two parallel shadow scales (--os-sh-* here and --sh-* in
     craft.css) that disagreed by a few points at every depth, and no inner
     hairline anywhere. Everything below this comment is the whole answer.
     Every other depth token in this file, in craft.css and in the shell is
     an alias onto one of these, so the owner tunes a surface here and
     nowhere else.

     WHY NEW NAMES. apps/style/chrome.css is injected by pekan/windows.js
     after every linked sheet and restates --os-r-win, --os-r-sheet and
     --os-r-dock on :root as literals, so a value set on those names here
     never paints. These names are declared nowhere else, so what is written
     here is what the browser draws.

     The ladder. Four steps and nothing between them.
       --r-window   a window, the dock slab, the start menu, the lock card
       --r-card     a card inside a window, a menu, a toast, a sheet
       --r-control  a button, a field, a chip, a tab
       --r-pill     the status pill and anything fully round
     ===================================================================== */
  --r-window: 14px;
  --r-card: 12px;
  --r-control: 8px;
  --r-pill: 999px;

  /* The recipe. One glass, at three heights: floating (a window, the dock),
     lifted (a menu, a toast, a sheet) and modal (the lock card). Each height
     is the same material with more blur, a lighter border and a longer
     ambient shadow. The inner hairline is the same on all three: it is the
     thickness of the glass, not its height. */
  --glass-blur: 22px;            /* floating */
  --glass-blur-lift: 32px;       /* lifted */
  --glass-blur-modal: 30px;      /* modal: the lock card reads the aurora */
  --glass-sat: 1.62;
  --glass-sat-lift: 1.70;
  --glass-sat-modal: 2.1;
  --glass-bg: rgba(var(--g2-rgb), var(--g2-a));
  --glass-bg-lift: rgba(var(--g3-rgb), var(--g3-a));
  --glass-border-col: rgba(255, 255, 255, 0.18);
  --glass-border-col-lift: rgba(255, 255, 255, 0.26);
  --glass-border-col-idle: rgba(255, 255, 255, 0.10);
  --glass-border: 1px solid var(--glass-border-col);
  --glass-border-lift: 1px solid var(--glass-border-col-lift);
  /* the inner hairline: 1 px inset at white 8 percent, on every floating
     surface. It is what makes a pane read as having a thickness. */
  --glass-inner: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
  /* the outer dark hairline: the edge that stops glass dissolving into a
     bright patch of wallpaper. A shadow, not a border, so it costs no layout. */
  --glass-edge: 0 0 0 1px rgba(0, 0, 0, 0.55);
  /* the lit top edge, one sun at 11 o'clock, brighter as the surface rises */
  --glass-rim-1: inset 0 1px 0 rgba(255, 255, 255, 0.10);
  --glass-rim-2: inset 0 1px 0 rgba(255, 255, 255, 0.16);
  --glass-rim-3: inset 0 1px 0 rgba(255, 255, 255, 0.22);
  --glass-rim-4: inset 0 1px 0 rgba(255, 255, 255, 0.28);
  /* two drop layers each: a tight contact shadow and a wide ambient one.
     The ambient roughly triples between steps; that ratio reads as height. */
  --glass-shadow-rest: 0 1px 2px rgba(0, 0, 0, 0.36), 0 4px 10px rgba(0, 0, 0, 0.22);
  --glass-shadow: 0 2px 6px rgba(0, 0, 0, 0.40), 0 16px 44px rgba(0, 0, 0, 0.34);
  --glass-shadow-focus: 0 4px 12px rgba(0, 0, 0, 0.44), 0 30px 76px rgba(0, 0, 0, 0.42);
  --glass-shadow-lift: 0 5px 14px rgba(0, 0, 0, 0.46), 0 34px 84px rgba(0, 0, 0, 0.44);
  --glass-shadow-modal: 0 8px 22px rgba(0, 0, 0, 0.50), 0 52px 120px rgba(0, 0, 0, 0.50);

  /* the dock, the numbers the owner will want to move */
  --dock-blur: var(--glass-blur);
  --dock-radius: var(--r-window);
  --dock-icon: 44px;             /* the artwork */
  --dock-tile: 50px;             /* the well the artwork sits in */
  --dock-gap: 8px;
  --dock-mag: 1.12;              /* hover peak, pekan/dock.js reads it */
  --dock-lift: 18px;             /* gap from the bottom edge */

  /* the top bar: one size and one weight for the clock, wifi, battery, speaker */
  --bar-font: 13px;
  --bar-weight: 600;

  /* window motion: opening scales up from 0.98 and closing back to it, in
     the 180 to 220 ms band. pekan/windows.js owns the spring itself; these
     are the numbers this sheet uses for its own transitions and the
     numbers the spring is asked to match. */
  --win-open-ms: 200ms;
  --win-close-ms: 180ms;
  --win-scale-from: 0.98;
  /* an inactive window loses colour through its BACKDROP saturation, never
     through a layer filter, so the ink stays as crisp as on the active one
     (plan 4.3, and section 5 further down). Active glass lifts to 1.62;
     inactive glass sits near neutral. */
  --glass-sat-idle: 1.12;

  /* the glass system, depth 1: sunk. Wallpaper and desktop.
     Plan 4.1: a near black carrying the brand tint at high chroma and low
     lightness, so the base reads as Pekan and never as a flat black. */
  /* True black. The four tints below sit ON this, so the base is what the
     eye reads in every quiet region of the desk. */
  --g1-base: #000000;
  --g1-warm: #141018;
  /* Measured before: 40, 32, 18 and 16 percent, which is why a vision pass
     read the ground as navy, purple and teal rather than as black. The
     wallpaper keeps its structure and loses its chroma: one lavender, one
     deeper lavender, and the two foreign hues (amber, teal) dropped to a
     level that shapes the light without naming a colour. */
  --g1-tint-a: rgba(131, 138, 255, 0.09);
  --g1-tint-b: rgba(89, 86, 233, 0.07);
  --g1-tint-c: rgba(255, 255, 255, 0.03);
  --g1-tint-d: rgba(131, 138, 255, 0.04);
  --g1-blur: 0px;
  --g1-border: none;
  --g1-shadow: none;

  /* depth 2: floating. Windows, dock, sticky notes.
     Plan 4.1: rgba(22, 18, 32, 0.62), blur 20 px. The alpha starts at the
     spec's 62 percent and is raised by pekan/glass.js when a wallpaper
     would break the contrast floor for light ink. */
  --g2-rgb: 22, 18, 32;
  --g2-a: 0.62;
  --g2-bg: var(--glass-bg);
  /* round 11: every depth 2 number is an alias onto the glass recipe above */
  --g2-blur: var(--glass-blur);
  --g2-sat: var(--glass-sat);
  --g2-border: var(--glass-border);
  --g2-border-col: var(--glass-border-col);
  --g2-shadow: var(--glass-shadow);
  --g2-shadow-focus: var(--glass-shadow-focus);
  /* plan 4.3: an inactive window keeps the blur and drops to 0.50 with a
     dimmer border, so the wallpaper reads through it */
  --g2-a-idle: 0.50;
  --g2-bg-idle: rgba(var(--g2-rgb), var(--g2-a-idle));
  --g2-border-idle: var(--glass-border-col-idle);

  /* depth 3: lifted. Menus, dialogs, notifications.
     Plan 4.1: rgba(28, 22, 42, 0.78), blur 32 px. */
  --g3-rgb: 28, 22, 42;
  --g3-a: 0.78;
  --g3-bg: var(--glass-bg-lift);
  /* round 11: depth 3 is the lifted height of the one glass recipe */
  --g3-blur: var(--glass-blur-lift);
  --g3-sat: var(--glass-sat-lift);
  --g3-border: var(--glass-border-lift);
  --g3-border-col: var(--glass-border-col-lift);
  --g3-shadow: var(--glass-shadow-lift);

  /* ink over glass: plan 4.1 */
  --os-text: #F3F0FA;
  --os-text-2: #C4C0D0;
  --os-text-3: #9E9AAC;
  --os-line: rgba(255, 255, 255, 0.10);
  --os-line-2: rgba(255, 255, 255, 0.18);
  --os-line-3: rgba(255, 255, 255, 0.28);
  --os-tile: rgba(255, 255, 255, 0.06);
  --os-tile-2: rgba(255, 255, 255, 0.12);
  --os-pane: var(--g3-bg);
  --os-good: #5FD3A0;
  /* --os-warn and --os-focus carry the same literal as --pm-sun and
     --pm-lavender today and are deliberately NOT aliased to them. A status
     colour and a focus ring answer to a contrast floor; a brand hue answers
     to the brand. Coupling them would move the focus ring the next time the
     accent is retuned. Plan 4.1 states the focus ring value directly. */
  --os-warn: #E4C56A;
  --os-bad: #FF8A8F;
  --os-focus: #A99BFF;
  /* the halo behind a focus ring is dark on dark, not white on dark */
  --os-focus-halo: rgba(12, 10, 18, 0.85);

  --os-ui: var(--pk-font);
  --os-body: var(--pk-font);
  --os-mono: var(--pk-font);

  /* radii, plan 4.2. Measured system numbers, the numbers only: no Apple
     artwork, no SF Pro, no traffic light drawings, no squircle paths. */
  /* Round 11: the plan 4.2 names are kept for every sheet that reads them,
     and each is now a rung of the ladder above rather than its own number.
     apps/style/chrome.css restates these on :root after this sheet, so the
     surfaces THIS sheet paints read the ladder names directly. */
  --os-r-win: var(--r-window);      /* app window */
  --os-r-sheet: var(--r-card);      /* sheet and dialog */
  --os-r-menu: var(--r-card);       /* menu and context menu */
  --os-r-btn: var(--r-control);     /* push button */
  --os-r-cta: var(--r-control);     /* primary action */
  --os-r-icon: var(--r-window);     /* 64 px icon well agrees with the window */
  --os-r-dock: var(--dock-radius);  /* dock slab */
  --os-r-toast: var(--r-card);      /* a toast is a small card */
  --os-r-win98: 2px;     /* Windows 98 dialog, 2 so retina is not broken */
  --os-r-ios3: 10px;     /* iPhone OS 3 alert */
  --os-chrome-h: 38px;
  --os-r-ctl: var(--r-control);
  --os-r-tile: var(--r-card);

  /* the wallpaper reads these. Plan 4.4: motion slower, speed 0.65. */
  /* pekan/wallpaper.js multiplies every region it paints by this. At 1 the
     animated layer covers the whole desk, which is what made the base read
     as an indigo picture rather than as black with light on it. */
  --os-wall-opacity: 0.42;
  --os-wall-speed: 0.65;
  --os-wall-caustic: 24s;
  --os-wall-dust: 0.04;

  /* retune the shell's own variables so every native app comes with us */
  --text: var(--os-text);
  --text2: var(--os-text-2);
  --bg: #12101A;
  --bg50: rgba(22, 18, 32, 0.55);
  --bg70: var(--g2-bg);
  --shadow: rgba(0, 0, 0, 0.55);
  --card: rgba(255, 255, 255, 0.05);
  --hover: rgba(255, 255, 255, 0.08);
  --hover-half: rgba(255, 255, 255, 0.05);
  --hover-b: rgba(255, 255, 255, 0.06);
  --bggrey: rgba(28, 22, 42, 0.72);
  --fill: rgba(255, 255, 255, 0.10);
  --bgul: var(--g1-base);
  --mm: rgba(28, 22, 42, 0.86);
  --contextmeu: var(--g3-bg);
  --bar: rgba(255, 255, 255, 0.05);
  --hr: var(--os-line-2);
  --unfoc: rgba(22, 18, 32, 0.62);
  --msg: var(--g3-bg);
  --theme-1: var(--pm-purple);
  --theme-2: var(--pm-purple-deep);
  --href: var(--pm-lavender);
  --bd: rgba(255, 255, 255, 0.08);
  --s3d: rgba(0, 0, 0, 0.42);
  --mica: none;
  --pd-violet: var(--pm-purple);
  --pd-violet-deep: var(--pm-purple-deep);
  --pd-grad: var(--pm-purple);
  --pd-font: var(--os-ui);
  color-scheme: only dark;
}

/* Blur is expensive and it is never free. Under a reduced transparency
   preference every depth drops to flat translucency: no backdrop filter,
   opaque enough to stand on its own. Plan 4.1 pins this at alpha 0.94 and
   no blur; depth 3 sits a little above it because it stacks on depth 2. */
@media (prefers-reduced-transparency: reduce) {
  :root {
    --g2-a: 0.94;
    --g2-a-idle: 0.94;
    --g2-blur: 0px;
    --g3-a: 0.97;
    --g3-blur: 0px;
    --g2-border-col: rgba(255, 255, 255, 0.22);
    --g3-border-col: rgba(255, 255, 255, 0.30);
    --g2-border: 1px solid rgba(255, 255, 255, 0.22);
    --g3-border: 1px solid rgba(255, 255, 255, 0.30);
    --g2-border-idle: rgba(255, 255, 255, 0.14);
  }
  .os-g2, .os-g3, .window, .window.foc, .window.webapp, .window.webapp.foc,
  :root.mica .window, :root.mica .window.foc, :root.mica .window.webapp,
  :root.mica .window.webapp.foc,
  #dock-box, #os-halo, #start-menu, #cm, #dp,
  .pk-toast, #notice, .os-note, #loginback, #loginback > .lock-core,
  #os-note-undo, #os-setup, #os-setup .card {
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
  }
}

body { background: var(--g1-base); font-family: var(--os-body); color: var(--os-text); }
b, strong, h1, h2, h3, h4, button, .btn, .a { font-family: var(--os-ui); }

/* Two helpers so a component picks a depth and inherits it. Nothing below
   restates a surface; it uses one of these or the tokens directly. */
.os-g2 {
  background: var(--g2-bg);
  border: var(--g2-border);
  box-shadow: var(--g2-shadow);
  backdrop-filter: blur(var(--g2-blur)) saturate(var(--g2-sat));
  -webkit-backdrop-filter: blur(var(--g2-blur)) saturate(var(--g2-sat));
}
.os-g3 {
  background: var(--g3-bg);
  border: var(--g3-border);
  box-shadow: var(--g3-shadow);
  backdrop-filter: blur(var(--g3-blur)) saturate(var(--g3-sat));
  -webkit-backdrop-filter: blur(var(--g3-blur)) saturate(var(--g3-sat));
}

/* One focus ring, and it survives translucency. Plan 4.1: #A99BFF, 2 px,
   offset 2. The ring is light so it reads against dark glass.

   WCAG 2.1 SC 1.4.11 asks a focus indicator to clear 3:1 against what is
   adjacent to it, and on a drifting aurora "adjacent" is not a fixed
   colour. So the ring gets a dark halo on both sides of itself rather
   than behind it: the outline occupies 2 px to 4 px out from the border
   box, and the halo spreads to 6 px, which leaves 2 px of known dark
   outside the ring as well as inside it. The ring then never has to fight
   whatever the wallpaper is doing underneath, and the measured number
   holds wherever the window is dragged. */
:focus-visible {
  outline: 2px solid var(--os-focus);
  outline-offset: 2px;
  box-shadow: 0 0 0 6px var(--os-focus-halo);
  border-radius: 4px;
}

/* ===== wallpaper: depth 1 ===== */
#bg-anim, body:not(.has-video-wallpaper) #bg-video { display: none !important; }
#bg-lava { position: fixed; inset: 0; width: 100%; height: 100%; z-index: 0; display: block; }
/* the grain sits over the aurora because banding is worse on dark: a wide
   near black gradient over 16 million colours will step visibly without
   it. The vignette pulls the corners down so the glass has somewhere to
   lift away from. */
/* Round 11: the horizon glow. One wide band low on the screen, plum
   warmed toward the brand lavender, sitting where a skyline would. It is
   the one still element in a drifting field, so the eye has a ground to
   stand on. Tunable: height, position and strength. */
:root {
  --os-wall-horizon: 0.16;       /* peak alpha of the band */
  --os-wall-horizon-y: 74%;      /* where the band sits, top of viewport is 0 */
  --os-wall-horizon-h: 30%;      /* band height as a share of the viewport */
  --os-wall-grain: 0.02;         /* pekan/wallpaper.js reads it: 2 percent */
  --os-wall-parallax: 3;         /* pekan/wallpaper.js reads it: px of pointer parallax */
}
#bg-scrim {
  z-index: 0 !important;
  background:
    radial-gradient(130% 105% at 50% 44%, transparent 54%, rgba(0, 0, 0, 0.34) 100%),
    radial-gradient(ellipse 120% var(--os-wall-horizon-h) at 50% var(--os-wall-horizon-y),
      rgba(168, 148, 255, var(--os-wall-horizon)) 0%,
      rgba(140, 96, 232, calc(var(--os-wall-horizon) * 0.55)) 38%,
      rgba(110, 64, 168, 0) 100%),
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.045 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>") !important;
  opacity: 1 !important;
}
#desktop-clock { display: none !important; }

/* ===== desktop icons: quiet, legible on dark ===== */
#desktop { padding: 16px 0 0 14px; }
#desktop > div { width: 92px; height: auto; min-height: 84px; margin: 4px 2px 2px 2px; padding: 8px 4px 6px; border-radius: var(--os-r-icon); transition: background 180ms var(--pm-ease); }
#desktop > div:hover { background: rgba(255, 255, 255, 0.08); }
#desktop > div.sel { background: rgba(110, 90, 232, 0.26); outline: 1px solid rgba(169, 155, 255, 0.52); }
#desktop > div > img, #desktop > div.b > img { width: 44px; height: 44px; margin: 0 0 6px; filter: none; }
/* the label carries a dark plate rather than a light one, so it holds over
   the brightest patch the aurora can put behind it */
#desktop > div > p { font-family: var(--os-ui); font-size: 11.5px; font-weight: 550; line-height: 1.25; color: var(--os-text); text-shadow: 0 1px 3px rgba(0, 0, 0, 0.72), 0 0 10px rgba(0, 0, 0, 0.42); margin: 0; white-space: normal; overflow-wrap: anywhere; }
#desktop > div.sel > p { color: var(--os-text); }
#desktop.mini > div { width: 72px; }
#desktop > .selection { border: 1px solid var(--pm-lavender); background: rgba(110, 90, 232, 0.20); border-radius: 4px; }

/* ===== the Halo: depth 3 ===== */
#os-halo {
  position: fixed; top: 16px; right: 20px; z-index: 93;
  display: flex; align-items: center; gap: 12px;
  height: 42px; padding: 0 6px 0 14px;
  border-radius: var(--r-pill);
  background: var(--g3-bg);
  border: var(--g3-border);
  backdrop-filter: blur(var(--g3-blur)) saturate(var(--g3-sat));
  -webkit-backdrop-filter: blur(var(--g3-blur)) saturate(var(--g3-sat));
  /* plan 4.3: the halo is already an island, retuned here for light text.
     The inner highlight is the top edge catching the aurora. */
  box-shadow: var(--g3-shadow), inset 0 1px 0 rgba(255, 255, 255, 0.22);
  color: var(--os-text);
  font-family: var(--os-ui); font-size: var(--bar-font); font-weight: var(--bar-weight);
  transition: height 300ms var(--pm-ease), padding 300ms var(--pm-ease);
  cursor: default;
}
#os-halo .sep { width: 1px; height: 16px; background: var(--os-line-2); }
#os-halo .wx { display: flex; align-items: center; gap: 7px; color: var(--os-text-2); font-size: var(--bar-font); font-weight: var(--bar-weight); }
#os-halo .wx i { width: 12px; height: 12px; border-radius: 50%; border: 1.5px solid var(--os-text-2); position: relative; display: inline-block; }
#os-halo .wx i::after { content: ""; position: absolute; inset: 2.5px; border-radius: 50%; background: var(--os-text-2); }
/* round 11: the clock takes the bar's one size and weight, in the UI face
   rather than the mono, so it sits on the same line as the wifi and the
   battery instead of reading as a terminal readout beside them */
#os-halo .time { font-family: var(--os-ui); font-size: var(--bar-font); font-weight: var(--bar-weight); letter-spacing: 0.01em; font-variant-numeric: tabular-nums; }
#os-halo .bat { display: inline-flex; align-items: center; color: var(--os-text-2); }
#os-halo .bat i { width: 20px; height: 10px; border: 1.5px solid var(--os-text-3); border-radius: 3px; position: relative; display: inline-block; }
#os-halo .bat i::after { content: ""; position: absolute; left: 1.5px; top: 1.5px; bottom: 1.5px; width: var(--bat, 62%); background: var(--os-text-2); border-radius: 1px; }
#os-halo .bat i::before { content: ""; position: absolute; right: -4px; top: 2px; width: 2px; height: 4px; background: var(--os-text-3); border-radius: 1px; }
#os-halo .av { width: 30px; height: 30px; border-radius: 50%; background: rgba(255, 255, 255, 0.10); color: var(--os-text); font-weight: 700; font-size: 13px; display: grid; place-items: center; overflow: hidden; box-shadow: 0 0 0 1.5px rgba(255, 255, 255, 0.28); }
#os-halo .av img { width: 100%; height: 100%; object-fit: cover; }
#os-halo .note { display: none; align-items: center; gap: 10px; padding-right: 4px; max-width: 360px; }
#os-halo .note > div { min-width: 0; }
#os-halo.say { height: 48px; padding-left: 10px; }
#os-halo.say .note { display: flex; }
#os-halo .note .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--pm-purple); flex: none; }
#os-halo .note b { display: block; font-size: 12.5px; font-weight: 700; line-height: 1.15; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--os-text); }
#os-halo .note span { display: block; font-family: var(--os-body); font-size: 11.5px; color: var(--os-text-2); line-height: 1.2; margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
body.locked #os-halo { display: none; }

/* ===== the dock: depth 2, floating clear of the bottom edge =====
   Plan 4.3: 12 px gap from the bottom, inner highlight at white 22 percent
   so the top edge of the pill catches the light. Plan 4.2: 16 px radius,
   pulled back from round 4's 20 so it reads like a real dock. */
#dock-box {
  /* 18px of clearance, not 12. The pixel review measured roughly 11px and
     called the dock tight to the bottom edge, which costs it the floating
     read the whole material depends on. */
  bottom: var(--dock-lift); height: calc(var(--dock-tile) + 14px); padding: 7px 9px; gap: var(--dock-gap);
  border-radius: var(--dock-radius);
  /* round 11: the slab is glass with a soft top highlight, a gradient
     rather than a flat fill so the top edge is lighter than the bottom */
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.07), rgba(255, 255, 255, 0.015) 45%, rgba(255, 255, 255, 0) 100%),
    var(--glass-bg);
  border: var(--glass-border);
  outline: 0;
  backdrop-filter: blur(var(--dock-blur)) saturate(var(--glass-sat));
  -webkit-backdrop-filter: blur(var(--dock-blur)) saturate(var(--glass-sat));
  /* one outer stroke: the border. --glass-edge drew a second, darker ring
     immediately outside it. */
  box-shadow: var(--glass-shadow), var(--glass-inner);
  align-items: center;
  width: max-content;
  left: 50%;
  transform: translateX(-50%);
  justify-content: flex-start;
}
#dock-box > .dock { padding: 0; gap: var(--dock-gap); height: 100%; background: none; backdrop-filter: none; -webkit-backdrop-filter: none; box-shadow: none; outline: 0; align-items: center; display: flex; }
#dock-box > .dock.date, #dock-box > .dock.control, #dock-box > #toolbar { display: none !important; }
#dock-box .dock-divider { width: 1px; height: 26px; background: var(--os-line-2); margin: 0 6px; }

/* a dock tile: real icon artwork on a strict grid, no jitter */
#dock-box .dock-btn, #dock-box .dock-btn.pin {
  width: var(--dock-tile); height: var(--dock-tile); border-radius: var(--r-card); margin: 0; padding: 0;
  background: none; border: 0;
  display: grid; place-items: center; position: relative;
  transition: transform 160ms var(--pm-spring);
  transform-origin: 50% 100%;
  color: var(--os-text);
}
#dock-box .dock-btn > img, #dock-box .dock-btn.pin > img { width: var(--dock-icon); height: var(--dock-icon); filter: none; pointer-events: none; }
#dock-box .dock-btn > svg { width: 24px; height: 24px; fill: none; stroke: var(--os-text); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
/* subtle, not a theatrical swell */
#dock-box .dock-btn:hover, #dock-box .dock-btn.show, #dock-box .dock-btn:focus-visible { transform: scale(1.12); background: none; }
#dock-box .dock-btn:focus-visible { outline: 2px solid var(--os-focus); outline-offset: 2px; box-shadow: 0 0 0 6px var(--os-focus-halo); }
#dock-box .dock-btn.on > svg { stroke: var(--pm-lavender); }
#dock-box #start-btn { width: auto; padding: 0 12px 0 9px; gap: 9px; display: flex; height: var(--dock-icon); border-radius: var(--r-card); background: rgba(255, 255, 255, 0.08); border: 1px solid var(--os-line-2); }
#dock-box #start-btn:hover { transform: scale(1.06); background: rgba(255, 255, 255, 0.16); }
#dock-box #start-btn .pd-mark { width: 22px; height: 22px; filter: none; }
#dock-box #start-btn .start-label { font-family: var(--os-ui); font-size: 12.5px; font-weight: 700; color: var(--os-text); }
#dock-box #taskbar { display: flex !important; gap: 8px; width: auto !important; overflow: visible; padding: 0; background: none; backdrop-filter: none; -webkit-backdrop-filter: none; box-shadow: none; outline: 0; align-items: center; }
#dock-box #taskbar > a, #dock-box #taskbar .dock-btn { width: var(--dock-tile); height: var(--dock-tile); border-radius: var(--r-card); background: none; border: 0; margin: 0; padding: 0; display: inline-grid; place-items: center; position: relative; transition: transform 160ms var(--pm-spring); transform-origin: 50% 100%; }
#dock-box #taskbar > a:hover { transform: scale(1.12); background: none; }
#dock-box #taskbar > a img { width: 40px; height: 40px; object-fit: contain; margin: 0; filter: none; }
/* the running indicator is a Pekan dot, not a glow */
#dock-box #taskbar > a::after, #dock-box .dock-btn.run::after {
  content: ""; position: absolute; bottom: 1px; left: 50%; transform: translateX(-50%);
  width: 5px; height: 5px; border-radius: 50%; background: var(--pm-lavender);
}
#dock-box #taskbar > a.foc::after, #dock-box .dock-btn.run.foc::after { width: 14px; border-radius: 3px; }
#dock-box #taskbar > a.foc, #dock-box #taskbar > a.pin { background: none; box-shadow: none; }
#dock-box .dock-key {
  height: 42px; padding: 0 17px; border-radius: var(--os-r-cta); margin-left: 4px;
  background: var(--pm-purple); color: #fff; font-family: var(--os-ui); font-weight: 700; font-size: 13.5px;
  display: flex; align-items: center; gap: 8px; white-space: nowrap; cursor: pointer;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.42);
  transition: background 180ms var(--pm-ease), transform 160ms var(--pm-spring);
}
#dock-box .dock-key:hover { background: var(--pm-purple-deep); color: #14101C; transform: translateY(-2px); }
#dock-box .dock-key svg { width: 15px; height: 15px; }
#open-dock { background: var(--g3-bg); outline: 1px solid var(--g3-border-col); color: var(--os-text); box-shadow: var(--g3-shadow); }

#voiceBall { display: none !important; }

/* ===== windows: rectangles, 10 px radius, depth 2 glass ===== */
/* The shell's own visual pass pins the frame at 12 px with a dark border
   and a heavy black shadow, all with importants. The plan asks for a 10 px
   rectangle on light glass, so those three are taken back here. */
.window {
  background: var(--glass-bg);
  border: var(--glass-border) !important;
  /* round 11: the ladder name, not the plan alias, because chrome.css
     restates the alias after this sheet. See the token block. */
  border-radius: var(--r-window) !important;
  box-shadow: var(--glass-edge), var(--glass-shadow), var(--glass-inner) !important;
  color: var(--os-text);
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  transition: transform var(--win-open-ms) var(--pm-ease), opacity var(--win-open-ms) var(--pm-ease);
}
/* plan 4.3: an inactive window keeps the same blur, drops to alpha 0.50
   and takes a dimmer border. The wallpaper must read through it. No
   opacity trick and no heavy dimming: the surface itself gets thinner. */
.window:not(.foc) {
  background: var(--g2-bg-idle);
  border-color: var(--glass-border-col-idle) !important;
  box-shadow: var(--glass-edge), var(--glass-shadow), var(--glass-inner) !important;
  /* round 11: the colour loss lives in the backdrop, see --glass-sat-idle */
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat-idle));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat-idle));
  opacity: 1;
}
.window.foc {
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  box-shadow: var(--glass-edge), var(--glass-shadow-focus), var(--glass-inner);
  border-color: var(--glass-border-col-lift) !important;
}
/* a web app window is glass like any other, so it takes the depth back from
   the shell's own rule, which clears the backdrop filter for these */
.window.webapp, .window.webapp.load, .window.webapp.content {
  background: var(--g2-bg);
  backdrop-filter: blur(var(--g2-blur)) saturate(var(--g2-sat));
  -webkit-backdrop-filter: blur(var(--g2-blur)) saturate(var(--g2-sat));
}
.window.webapp > .titbar { outline: 0; margin-bottom: 0; }
.window.max { border-radius: 0; border-width: 0; }
/* the shell keeps two appearance switches on the root, mica and squircle
   corners, from the build this is themed over. The glass system is the one
   appearance the OS ships, so mica may not re-enter here: it cancels the
   backdrop filter with an important.

   The corner rules below take the shell's RADIUS back so the measured table
   in plan 4.2 governs, and nothing else. Each once also carried
   "corner-shape: normal", which was removed on 2026-09-03: there is no such
   keyword, the parser dropped every one, and they had never had any effect
   (round 8 chrome lane, docs/evidence/round8/chrome/CHECK.md section 3).

   They were NOT rewritten to "round". apps/style/chrome.css is injected by
   pekan/windows.js after this sheet and pins the OS surfaces to squircle
   unconditionally, because round 8 settled that Apple rounds continuously
   everywhere. Writing "round" here would have inverted the visitor facing
   control: the switch is labelled "Smooth rounded corners", so checking it
   must give the continuous corner, not the circular one. Measured both ways
   before this was left alone. */
:root.corner_squ .window { border-radius: var(--r-window); }
:root.corner_squ .window.update, :root.corner_squ .window.diag { border-radius: var(--r-window); }
:root.corner_squ #cm { border-radius: var(--r-card); }
:root.corner_squ #start-menu { border-radius: var(--r-window); }
:root.corner_squ #desktop > div { border-radius: var(--r-window); }
:root.corner_squ #notice { border-radius: var(--r-card); }
:root.corner_squ .sm-app, :root.corner_squ list > * { border-radius: var(--r-control); }
:root.corner_squ .input, :root.corner_squ .btn, :root.corner_squ .button { border-radius: var(--r-control); }
:root.corner_squ #open-dock, :root.corner_squ .dock { border-radius: var(--dock-radius); }
:root.mica .window, :root.mica .window.foc, :root.mica .window.webapp,
:root.mica .window.webapp.foc {
  background: var(--g2-bg) !important;
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat)) !important;
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat)) !important;
}
/* round 11: the inactive window under the mica switch keeps the same blur
   at the idle saturation, so the active and inactive glass differ here too.
   The important is needed because the rule above carries one. */
:root.mica .window:not(.foc), :root.mica .window.webapp:not(.foc) {
  background: var(--g2-bg-idle) !important;
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat-idle)) !important;
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat-idle)) !important;
}
:root.mica .window.webapp.foc > .titbar, :root.mica .window.webapp > .titbar {
  background: rgba(255, 255, 255, 0.05) !important;
  backdrop-filter: none; -webkit-backdrop-filter: none;
}
/* the mica override above carries an important to beat the shell, so the
   reduced transparency rule has to carry one that lands later */
@media (prefers-reduced-transparency: reduce) {
  :root.mica .window, :root.mica .window.foc,
  :root.mica .window.webapp, :root.mica .window.webapp.foc,
  .window, .window.foc, .window.webapp, .window.webapp.foc {
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
  }
}

/* one 38 px chrome bar: controls left in Pekan colours, title centred */
.window > .titbar, .window.webapp > .titbar, .window.webapp.foc > .titbar, .window.chrome > .titbar {
  position: relative;
  min-height: var(--os-chrome-h); height: var(--os-chrome-h);
  background: rgba(255, 255, 255, 0.05) !important;
  border-bottom: 1px solid var(--os-line);
  border-radius: var(--r-window) var(--r-window) 0 0;
  color: var(--os-text); outline: 0; opacity: 1;
  padding: 0 12px 0 12px;
  align-items: center;
  backdrop-filter: none; -webkit-backdrop-filter: none;
  overflow: visible;
}
.window.max > .titbar { border-radius: 0; }
.window:not(.foc) > .titbar { opacity: 1; background: rgba(255, 255, 255, 0.03) !important; }
/* the title sits centred in the bar itself, so the controls do not shift it */
.window > .titbar > p {
  position: absolute; left: 96px; right: 96px; top: 0; height: var(--os-chrome-h);
  display: flex; align-items: center; justify-content: center; margin: 0;
  color: var(--os-text); font-family: var(--os-ui); font-size: 13px; font-weight: 700;
  letter-spacing: -0.01em; text-align: center; pointer-events: none;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex-grow: 0;
}
.window:not(.foc) > .titbar > p { color: var(--os-text-2); }
.window > .titbar > p small { font-weight: 500; color: var(--os-text-3); margin-left: 6px; }
/* no icon clutter in the bar */
.window > .titbar > .icon { display: none; }
/* the control group moves to the left of the bar */
.window > .titbar > div, .window > .titbar > div:not(.tabs) { order: -1; width: max-content; min-width: max-content; display: flex; flex-direction: row; align-items: center; align-self: center; gap: 8px; margin-right: auto; }
.window > .titbar .wbtg {
  width: 13px; height: 13px; min-width: 13px; border-radius: 50%;
  float: none; order: 3;
  margin: 0; padding: 0; display: grid; place-items: center;
  background: var(--os-line-3); color: transparent; font-size: 8px; line-height: 1;
  transition: background 160ms var(--pm-ease), color 160ms var(--pm-ease), transform 160ms var(--pm-spring);
  cursor: pointer;
}
.window > .titbar .wbtg i { font-size: 7px; line-height: 1; }
.window > .titbar .wbtg svg { width: 7px; height: 7px; }
/* close, then minimise, then maximise, in Pekan colours rather than the
   Apple three. The markup order is close, maximise, minimise, so the two
   on the right swap places here. */
.window > .titbar .wbtg.red { order: 1; }
.window > .titbar .wbtg:not(.red):not(.max) { order: 2; }
.window > .titbar .wbtg.max { order: 3; border-top-right-radius: 50%; }
/* plum, amber, teal. Not red, yellow, green, and not drawn the way Apple
   draws them: flat discs, our colours, our order. */
.window.foc > .titbar .wbtg.red { background: var(--pm-purple); }
.window.foc > .titbar .wbtg:not(.red):not(.max) { background: var(--pm-sun); }
.window.foc > .titbar .wbtg.max { background: #3E9C9C; }
.window > .titbar:hover .wbtg, .window > .titbar .wbtg:focus-visible { color: rgba(12, 10, 18, 0.80); }
.window > .titbar .wbtg.red:hover { background: var(--pm-purple-deep); color: #14101C; transform: scale(1.12); }
.window > .titbar .wbtg:not(.red):hover { transform: scale(1.12); color: rgba(12, 10, 18, 0.90); }
.window > .titbar .wbtg:focus-visible { outline: 2px solid var(--os-focus); outline-offset: 3px; box-shadow: 0 0 0 6px var(--os-focus-halo); }
.window > .content { background: transparent; }
.window > .loadback { background: rgba(12, 10, 18, 0.55); }
/* a third party page inside the OS browser keeps its own white ground; the
   frame around it is ours */
.window iframe { background: #fff; }

/* resizable by default from every edge and every corner. The knobs are
   wider than the shell's originals so an edge is reachable with a mouse
   and with a thumb, and they sit above the content. */
.window > .resize-bar { z-index: 250 !important; }
.window.max > .resize-bar { display: none !important; }
.window > .resize-bar > .resize-knob { position: absolute; touch-action: none; }
.window > .resize-bar > .resize-knob.top { left: 14px; top: -3px; height: 9px; width: calc(100% - 28px); cursor: ns-resize; }
.window > .resize-bar > .resize-knob.bottom { left: 14px; bottom: -3px; height: 9px; width: calc(100% - 28px); cursor: ns-resize; }
.window > .resize-bar > .resize-knob.left { top: 14px; left: -3px; height: calc(100% - 28px); width: 9px; cursor: ew-resize; }
.window > .resize-bar > .resize-knob.right { top: 14px; right: -3px; height: calc(100% - 28px); width: 9px; cursor: ew-resize; }
.window > .resize-bar > .resize-knob.top-left { top: -3px; left: -3px; width: 17px; height: 17px; cursor: nwse-resize; }
.window > .resize-bar > .resize-knob.bottom-right { bottom: -3px; right: -3px; width: 17px; height: 17px; cursor: nwse-resize; }
.window > .resize-bar > .resize-knob.bottom-left { bottom: -3px; left: -3px; width: 17px; height: 17px; cursor: nesw-resize; }
.window > .resize-bar > .resize-knob.top-right { top: -3px; right: -3px; width: 17px; height: 17px; cursor: nesw-resize; }

/* the snap preview: an outline drawn before the window is let go */
#os-snap {
  position: fixed; z-index: 89; pointer-events: none;
  border-radius: var(--os-r-win);
  border: 2px solid var(--pm-lavender);
  background: rgba(110, 90, 232, 0.18);
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.45);
  opacity: 0; transform: scale(0.99);
  transition: opacity 140ms var(--pm-ease), transform 140ms var(--pm-ease),
              left 140ms var(--pm-ease), top 140ms var(--pm-ease),
              width 140ms var(--pm-ease), height 140ms var(--pm-ease);
}
#os-snap.on { opacity: 1; transform: none; }
#window-fill { display: none !important; }

/* ===== start menu, context menus, toasts, notices: depth 3 =====
   Plan 4.2 radii: a menu is 6 px, a sheet is 12 px, a toast is 8 px. */
#start-menu { background: var(--glass-bg-lift); border: var(--glass-border-lift); outline: 0; border-radius: var(--r-window); box-shadow: var(--glass-edge), var(--glass-shadow-lift), var(--glass-rim-4), var(--glass-inner); backdrop-filter: blur(var(--g3-blur)) saturate(var(--g3-sat)); -webkit-backdrop-filter: blur(var(--g3-blur)) saturate(var(--g3-sat)); color: var(--os-text); }
#start-menu .text, #start-menu p { color: var(--os-text-2); }
#start-menu a, #start-menu .sm-app { color: var(--os-text); border-radius: var(--r-control); }
#start-menu a:hover, #start-menu .sm-app:hover { background: rgba(255, 255, 255, 0.09); }
#start-menu img { filter: none; }
#cm, #dp { background: var(--glass-bg-lift); border: var(--glass-border-lift); outline: 0; border-radius: var(--r-card); box-shadow: var(--glass-edge), var(--glass-shadow-lift), var(--glass-rim-4), var(--glass-inner); backdrop-filter: blur(var(--g3-blur)) saturate(var(--g3-sat)); -webkit-backdrop-filter: blur(var(--g3-blur)) saturate(var(--g3-sat)); color: var(--os-text); }
#cm > list > a { color: var(--os-text); border-radius: var(--r-control); }
#cm > list > a:hover { background: rgba(255, 255, 255, 0.10); }
#pk-toasts { right: 20px; bottom: 100px; }
.pk-toast { background: var(--glass-bg-lift); border: var(--glass-border-lift); backdrop-filter: blur(var(--glass-blur-lift)) saturate(var(--glass-sat-lift)); -webkit-backdrop-filter: blur(var(--glass-blur-lift)) saturate(var(--glass-sat-lift)); border-radius: var(--r-card); color: var(--os-text); box-shadow: var(--glass-edge), var(--glass-shadow-lift), var(--glass-rim-4), var(--glass-inner); font-family: var(--os-body); }
.pk-toast h4, .pk-toast-app { color: var(--os-text); font-family: var(--os-ui); }
.pk-toast p { color: var(--os-text-2); }
.pk-toast-icon { border-radius: var(--os-r-btn); filter: none; }
.pk-toast button, .pk-toast .primary { background: var(--pm-purple); color: #fff; border-radius: var(--os-r-btn); font-family: var(--os-ui); font-weight: 700; border: 0; }
.pk-toast input { background: rgba(255, 255, 255, 0.08); border: 1px solid var(--os-line-2); color: var(--os-text); border-radius: var(--os-r-btn); }
#notice { background: var(--glass-bg-lift); backdrop-filter: blur(var(--glass-blur-lift)) saturate(var(--glass-sat-lift)); -webkit-backdrop-filter: blur(var(--glass-blur-lift)) saturate(var(--glass-sat-lift)); color: var(--os-text); border-radius: var(--r-card); outline: 0; border: var(--glass-border-lift); box-shadow: var(--glass-edge), var(--glass-shadow-lift), var(--glass-rim-4), var(--glass-inner); }
#notice-back { background: rgba(6, 5, 10, 0.52); }
.input, input.input, .btn, .button { border-radius: var(--os-r-btn); }
.btn, .button { background: var(--pm-purple); color: #fff; font-family: var(--os-ui); font-weight: 700; }
.btn:hover, .button:hover { background: var(--pm-purple-deep); color: #14101C; }
a { color: var(--pm-lavender); }
::selection { background: rgba(110, 90, 232, 0.42); color: var(--os-text); }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.20); border-radius: 6px; border: 2px solid transparent; background-clip: padding-box; }
::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.32); background-clip: padding-box; }
::-webkit-scrollbar-track { background: transparent; }

/* ===== sticky notes: depth 2 glass in Pekan colours ===== */
#os-sticky { display: none !important; }
#os-notes { position: fixed; inset: 0; z-index: 6; pointer-events: none; }
#os-notes > .os-note { pointer-events: auto; }
body.locked #os-notes { display: none; }
/* the undo that waits after a delete, depth 3 */
#os-note-undo {
  position: fixed; left: 50%; bottom: 100px; transform: translate(-50%, 10px);
  z-index: 140; display: flex; align-items: center; gap: 14px;
  padding: 10px 12px 10px 16px; border-radius: var(--os-r-sheet);
  background: var(--g3-bg); border: var(--g3-border); box-shadow: var(--g3-shadow);
  backdrop-filter: blur(var(--g3-blur)) saturate(var(--g3-sat));
  -webkit-backdrop-filter: blur(var(--g3-blur)) saturate(var(--g3-sat));
  color: var(--os-text); font-family: var(--os-ui); font-size: 13.5px; font-weight: 600;
  opacity: 0; transition: opacity 220ms var(--pm-ease), transform 220ms var(--pm-ease);
}
#os-note-undo.on { opacity: 1; transform: translate(-50%, 0); }
#os-note-undo button { height: 32px; padding: 0 14px; border: 0; border-radius: var(--os-r-btn); background: var(--pm-purple); color: #fff; font-family: var(--os-ui); font-weight: 700; font-size: 13px; cursor: pointer; }
#os-note-undo button:hover { background: var(--pm-purple-deep); color: #14101C; }
/* A note is paper on the desk, not another glass panel. Gold and the
   other pastels are the note itself. Dark ink sits on that paper so the
   reminder reads as a scrap someone stuck there, not a themed card. */
.os-note {
  position: fixed; z-index: 7;
  width: 232px; height: 176px; min-width: 168px; min-height: 120px;
  display: flex; flex-direction: column;
  border-radius: 2px;
  background: var(--note-bg, #F4D35E);
  border: 0;
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.18), 0 12px 28px rgba(0, 0, 0, 0.28);
  color: var(--note-ink, #1A1408);
  font-family: var(--os-ui); font-size: 13.5px; line-height: 1.55;
  overflow: hidden;
}
.os-note.drag, .os-note.sizing { transition: none; }
.os-note.foc { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.22), 0 16px 36px rgba(0, 0, 0, 0.28); }
.os-note > .nb {
  height: 28px; flex: none; display: flex; align-items: center; gap: 6px;
  padding: 0 7px 0 9px; cursor: grab; touch-action: none;
  border-bottom: 1px solid rgba(26, 20, 8, 0.12);
}
.os-note.drag > .nb { cursor: grabbing; }
/* The grip's TAP box and its HANDLE are two different boxes. A phone rule
   raises every note control to a 44px minimum, and while the grip drew its
   handle as its own background that minimum painted a 44px dark slab in the
   note header. The handle is now a centred pseudo element, so the tap box
   can be any size the layout needs. */
.os-note > .nb .grip { width: 22px; min-width: 22px; height: 100%; background: none; margin-right: auto; display: grid; place-items: center; }
.os-note > .nb .grip::before { content: ""; display: block; width: 22px; height: 3px; border-radius: 2px; background: rgba(26, 20, 8, 0.28); }
.os-note > .nb button { width: 20px; height: 20px; border: 0; border-radius: var(--os-r-menu); background: none; color: rgba(26, 20, 8, 0.55); display: grid; place-items: center; cursor: pointer; padding: 0; }
.os-note > .nb button:hover { background: rgba(26, 20, 8, 0.10); color: rgba(26, 20, 8, 0.85); }
.os-note > .nb button svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.os-note > .nb .sw { width: 14px; height: 14px; border-radius: 50%; border: 1px solid rgba(26, 20, 8, 0.28); cursor: pointer; padding: 0; }
.os-note > .nb .sw:focus-visible { outline: 2px solid var(--os-focus); outline-offset: 2px; }
.os-note > .body {
  flex: 1; min-height: 0; padding: 10px 12px 16px; overflow: auto;
  outline: none; color: var(--note-ink, #1A1408); font-weight: 600;
  white-space: pre-wrap; overflow-wrap: anywhere;
  -webkit-user-select: text; user-select: text; cursor: text;
}
.os-note > .body:empty::before { content: attr(data-ph); color: rgba(26, 20, 8, 0.42); font-weight: 500; }
.os-note > .body:focus-visible { outline: 2px solid var(--os-focus); outline-offset: -2px; box-shadow: none; }
.os-note > .grabber { position: absolute; right: 0; bottom: 0; width: 20px; height: 20px; cursor: nwse-resize; touch-action: none; }
.os-note > .grabber::after { content: ""; position: absolute; right: 4px; bottom: 4px; width: 8px; height: 8px; border-right: 2px solid rgba(26, 20, 8, 0.35); border-bottom: 2px solid rgba(26, 20, 8, 0.35); border-radius: 0 0 3px 0; }

/* ===== the lock screen: one notification you would actually receive ===== */
#loginback {
  background: rgba(12, 10, 18, 0.42);
  backdrop-filter: blur(26px) saturate(1.25);
  -webkit-backdrop-filter: blur(26px) saturate(1.25);
  padding: 20px; gap: 0;
  align-items: center; justify-content: center;
}
.lock-clock { display: none !important; }
#os-lock-notes { display: none !important; }
#os-lock-proof { display: none !important; }
/* plan 4.3: the lock card is depth 3, 12 px radius, 1 px white 28 percent */
#loginback > .lock-core {
  width: min(430px, 100%); padding: 0; border-radius: var(--r-window);
  display: flex; flex-direction: column; align-items: stretch;
  position: relative; isolation: isolate;
  background:
    linear-gradient(160deg, rgba(255, 255, 255, 0.10) 0%, rgba(255, 255, 255, 0) 42%),
    var(--g3-bg);
  border: 1px solid rgba(255, 255, 255, 0.28);
  backdrop-filter: blur(var(--g3-blur)) saturate(var(--g3-sat));
  -webkit-backdrop-filter: blur(var(--g3-blur)) saturate(var(--g3-sat));
  /* box-shadow is NOT set here: craft.css depth 4 owns it and loads later.
     The rim lives there, beside the shadow it belongs to. */
  text-align: left; overflow: hidden;
}
/* the notification header: app icon, app name, and when it arrived */
#loginback > .lock-core > .lock-head { display: flex; align-items: center; gap: 9px; padding: 13px 16px 0; }
#loginback > .lock-core > .lock-head .ico { width: 26px; height: 26px; border-radius: var(--os-r-menu); flex: none; display: grid; place-items: center; background: rgba(255, 255, 255, 0.10); border: 1px solid var(--os-line-2); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.40); overflow: hidden; }
#loginback > .lock-core > .lock-head .ico img { width: 18px; height: 18px; object-fit: contain; }
#loginback > .lock-core > .lock-head .app { font-family: var(--os-ui); font-size: 12px; font-weight: 700; letter-spacing: 0.02em; text-transform: uppercase; color: var(--os-text-2); }
#loginback > .lock-core > .lock-head .when { margin-left: auto; font-family: var(--os-mono); font-size: 11.5px; color: var(--os-text-3); font-variant-numeric: tabular-nums; }
#loginback > .lock-core > .user { display: none; }
#loginback > .lock-core > .name {
  font-family: var(--os-ui); font-size: 19px; font-weight: 700; letter-spacing: -0.02em;
  color: var(--os-text); margin: 9px 0 0; padding: 0 16px; text-shadow: none; line-height: 1.25;
}
.lock-sub { color: var(--os-text-2); font-size: 13.5px; line-height: 1.5; margin: 5px 0 0; padding: 0 16px; max-width: none; }
#login-form { display: flex; gap: 8px; max-width: none; margin: 14px 0 0; padding: 0 16px; width: 100%; }
/* Round 9. The WRAPPER owns the border and focus ring; #login-phone below is
   deliberately bare so there are not two borders inside each other. */
/* Round 10. The field is a WHITE BOX on its own line, full width, the way a
   real sign in form looks. Dark text on white is deliberate: it is the one
   surface on this screen that must not read as glass. */
#login-form { flex-direction: column; align-items: stretch; gap: 10px; }
.pk-phone {
  width: 100%; display: flex; align-items: center; gap: 9px;
  height: 52px; padding: 0 14px;
  border: 1px solid rgba(0, 0, 0, 0.10); border-radius: var(--r-control);
  background: #FFFFFF;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.35), 0 6px 18px rgba(0, 0, 0, 0.28);
  transition: box-shadow 160ms, border-color 160ms;
}
.pk-phone:focus-within {
  border-color: transparent;
  outline: 2px solid var(--os-focus); outline-offset: 1px;
  box-shadow: 0 0 0 6px var(--os-focus-halo), 0 6px 18px rgba(0, 0, 0, 0.30);
}
/* the flag is set by pekan/lock.js from the area code as it is typed */
.pk-phone-flag {
  flex: none; font-size: 19px; line-height: 1; width: 24px; text-align: center;
  font-family: "Apple Color Emoji", "Segoe UI Emoji", sans-serif;
}
.pk-phone-cc {
  flex: none; font-family: var(--os-body); font-size: 15px; font-weight: 600;
  color: #3A3550; padding-right: 9px; border-right: 1px solid rgba(0, 0, 0, 0.14);
  line-height: 1;
}
#login-phone {
  flex: 1; min-width: 0; height: 100%; padding: 0;
  border: 0; background: none; box-shadow: none; border-radius: 0;
  color: #14101C; font-family: var(--os-body); font-size: 16px; font-weight: 500;
}
#login-phone::placeholder { color: #8A8598; font-weight: 400; }
#login-phone:focus, #login-phone:focus-visible { outline: none; box-shadow: none; }
/* the tick only ever appears on a valid number: lock.js adds .ok on input */
.pk-phone-ok {
  flex: none; font-size: 15px; color: #17A673;
  opacity: 0; transform: scale(0.7); transition: opacity 160ms, transform 160ms;
}
#login-phone.ok ~ .pk-phone-ok { opacity: 1; transform: scale(1); }
#login-form #login, #login-form button#login { width: 100%; justify-content: center; }
#login-form #login, #login { height: 44px; padding: 0 18px; border-radius: var(--r-control); background: var(--pm-purple); color: #fff; font-family: var(--os-ui); font-weight: 700; font-size: 14px; border: 0; box-shadow: 0 6px 16px rgba(0, 0, 0, 0.42); width: auto; }
#login:hover { background: var(--pm-purple-deep); color: #14101C; }
.lock-why { margin: 10px 0 0; padding: 0 16px; font-size: 12.5px; color: var(--os-text-2); }
/* Round 10. The headline is the joke, so it is the biggest thing on the card. */
.lock-bot {
  width: 62px; height: 62px; margin: 2px auto 10px; color: var(--os-text);
  opacity: 0.92; filter: drop-shadow(0 3px 12px rgba(0, 0, 0, 0.5));
}
.lock-bot svg { width: 100%; height: 100%; display: block; }
#loginback > .lock-core > .name {
  font-family: var(--os-ui); font-size: 26px; font-weight: 800;
  letter-spacing: -0.015em; line-height: 1.14; text-align: center;
  color: var(--os-text); margin: 0; padding: 0 18px;
  text-shadow: 0 2px 14px rgba(0, 0, 0, 0.45);
}
#login-form { padding: 16px 18px 0; }

/* the real reCAPTCHA iframe is a fixed 304px, so the row centres it rather
   than trying to stretch it */
.pk-cap { display: flex; justify-content: center; margin: 12px 0 0; padding: 0 18px; }
.pk-cap > div { max-width: 100%; }
.pk-cap.nudge { animation: pk-cap-nudge 320ms ease; }
@keyframes pk-cap-nudge { 0%,100% { transform: translateX(0); } 25% { transform: translateX(-5px); } 75% { transform: translateX(5px); } }

/* the offline stand in, shaped like the widget it replaces */
.pk-cap-fb {
  display: flex; align-items: center; gap: 11px; width: 100%;
  padding: 11px 13px; border: 1px solid var(--os-line-2);
  border-radius: var(--os-r-cta); background: rgba(255, 255, 255, 0.06);
}
.pk-cap-box {
  flex: none; width: 26px; height: 26px; padding: 0; position: relative;
  display: grid; place-items: center; cursor: pointer;
  border: 1px solid var(--os-line-3, rgba(255,255,255,0.34)); border-radius: 5px;
  background: rgba(255, 255, 255, 0.10);
}
.pk-cap-box > i { font-size: 15px; color: #46d39a; opacity: 0; transform: scale(0.6); transition: opacity 200ms, transform 200ms; }
.pk-cap.checked .pk-cap-box > i { opacity: 1; transform: scale(1); }
.pk-cap-spin { position: absolute; inset: 3px; border-radius: 50%; border: 2px solid transparent; border-top-color: var(--os-text-2); opacity: 0; }
.pk-cap.checking .pk-cap-spin { opacity: 1; animation: pk-human-turn 720ms linear infinite; }
.pk-cap.checking .pk-cap-box > i { opacity: 0; }
@keyframes pk-human-turn { to { transform: rotate(360deg); } }
.pk-cap-label { flex: 1; font-family: var(--os-ui); font-size: 13px; color: var(--os-text); }
.pk-cap-brand { flex: none; display: flex; flex-direction: column; align-items: center; gap: 3px; min-width: 46px; font-family: var(--os-ui); font-size: 8px; line-height: 1.2; letter-spacing: 0.04em; text-transform: uppercase; color: var(--os-text-3); text-align: center; }
.pk-cap-brand > i { font-size: 15px; color: var(--os-text-2); }
.pk-cap-box:focus-visible { outline: 2px solid var(--os-focus); outline-offset: 2px; }

/* the badge carousel, where the consent wall used to be */
.pk-marquee {
  margin: 16px 0 0; padding: 12px 0 13px; overflow: hidden;
  border-top: 1px solid var(--os-line);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}
.pk-marquee-row { display: flex; gap: 10px; width: max-content; animation: pk-marquee 26s linear infinite; }
.pk-marquee:hover .pk-marquee-row { animation-play-state: paused; }
@keyframes pk-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
.pk-chip {
  flex: none; display: flex; align-items: center; gap: 6px;
  padding: 6px 11px; border-radius: var(--r-control, 10px);
  border: 1px solid var(--os-line-2); background: rgba(255, 255, 255, 0.07);
  font-family: var(--os-ui); font-size: 11px; font-weight: 600;
  letter-spacing: 0.02em; color: var(--os-text-2); white-space: nowrap;
}
.pk-chip > i { font-size: 12px; color: var(--os-text); opacity: 0.85; }
@media (prefers-reduced-motion: reduce) {
  .pk-marquee-row { animation: none; }
  .pk-cap.checking .pk-cap-spin { animation: none; }
  .pk-cap.nudge { animation: none; }
}

.lock-consent {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 10px 0 0;
  padding: 0 16px;
  color: var(--os-text-2);
  font-family: var(--os-ui);
  font-size: 11.5px;
  font-weight: 500;
  line-height: 1.4;
  text-align: left;
  cursor: pointer;
}
/* The checkbox now lives inside #login-form, which already pads 18 px.
   Keep the outer padding for any leftover sibling, zero it here. */
#login-form .lock-consent {
  margin: 0;
  padding: 0;
}
.lock-consent input[type="checkbox"] {
  flex: none;
  width: 18px;
  height: 18px;
  min-width: 18px;
  margin: 2px 0 0;
  accent-color: var(--pm-purple, #6e5ae8);
}
.lock-consent input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--os-focus);
  outline-offset: 2px;
}
.lock-consent a { color: var(--os-text-2); }
.lock-guest,
#os-lock-guest {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin: 6px 0 0;
  padding: 0 16px;
  border: 0;
  background: none;
  color: var(--os-text-2);
  cursor: pointer;
  font: inherit;
  font-size: 12.5px;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.lock-guest:hover,
#os-lock-guest:hover { color: var(--os-text); }
.lock-guest:focus-visible,
#os-lock-guest:focus-visible {
  outline: 2px solid var(--os-focus);
  outline-offset: 2px;
}
.pk-cap-local-note {
  display: block;
  margin-top: 3px;
  font-size: 10.5px;
  font-weight: 500;
  color: var(--os-text-3, #9a94a8);
  letter-spacing: 0.01em;
}
/* the illustrative cue stays, under the card */
#os-lock-cue { width: min(430px, 100%); margin: 12px auto 0; padding: 0 2px; font-family: var(--os-mono); font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--os-text-2); text-shadow: 0 1px 3px rgba(12, 10, 18, 0.9); text-align: center; }
/* the badge strip artwork is dark on light, so it keeps its own light
   plate rather than being recoloured */
/* Round 9. The plate is gone: pekan-assets/badge-light.png carries light ink,
   so the wordmark sits on the glass itself. Larger, and lifted off the dark
   wallpaper by a shadow rather than by a box. */
/* The badge art is dark ink on light, so it keeps a light plate. Measured: the
   four badges are not one artwork, Facebook is 81 percent dark and Shopify 91,
   so no single recolour can serve all four. The plate is the honest answer. */
#login-badge { right: 20px; bottom: 20px; left: auto; max-height: 54px; padding: 9px 13px; border-radius: var(--r-control); background: rgba(255, 255, 255, 0.94); opacity: 1; box-shadow: 0 8px 26px rgba(0, 0, 0, 0.42), inset 0 1px 0 rgba(255, 255, 255, 0.9); filter: none; }
#loginback > .langselect.new { left: 20px; bottom: 20px; right: auto; background: var(--g3-bg); border: var(--g3-border); box-shadow: var(--glass-edge), var(--glass-shadow-lift), var(--glass-rim-3), var(--glass-inner); border-radius: var(--r-pill); padding: 3px; backdrop-filter: blur(var(--g3-blur)); -webkit-backdrop-filter: blur(var(--g3-blur)); }
#loginback > .langselect.new > .a { border-radius: var(--r-pill); color: var(--os-text-2); font-family: var(--os-ui); font-weight: 700; font-size: 12px; padding: 5px 12px; }
#loginback > .langselect.new > .a.selected { background: var(--pm-purple); color: #fff; }
.lock-site { bottom: 24px; background: var(--g3-bg); border: var(--g3-border); color: var(--os-text-2); width: 36px; height: 36px; box-shadow: var(--g3-shadow); }
#login-welc { background: var(--g1-base); color: var(--os-text); font-family: var(--os-ui); }

/* ===== phone ===== */
/* Tablet. The shell opens every window at 94vw with an important, which is
   the right default here but also outranks maximise, so a window snapped to
   the top landed short of the screen. Maximise takes it back. */
@media (min-width: 621px) and (max-width: 1100px) {
  .window.window.max {
    left: 0 !important;
    top: 0 !important;
    width: 100% !important;
    height: 100% !important;
  }
  .window > .resize-bar { display: block; }
  .window > .titbar .wbtg { width: 15px; height: 15px; min-width: 15px; font-size: 8px; }
  .window > .titbar { height: var(--os-chrome-h); }
}

@media (max-width: 700px) {
  #os-halo { top: 12px; right: 12px; left: 12px; height: 40px; padding: 0 6px 0 12px; font-size: 12px; }
  #os-halo.say { height: 40px; }
  #os-halo .note span, #os-halo .wx span, #os-halo .sep { display: none; }
  #os-halo .note b { max-width: 170px; font-size: 12px; }
  #os-halo .time { margin-right: auto; }
  /* eight apps plus Start do not fit across a phone, so the row scrolls
     rather than shrinking every target below the 44 px minimum */
  #dock-box { left: 10px; right: 10px; width: auto; max-width: none; transform: none; bottom: 12px; padding: 7px 8px; border-radius: var(--os-r-dock); justify-content: flex-start; height: 60px; gap: 0; overflow: hidden; }
  #dock-box > .dock.dock-start { gap: 4px; overflow-x: auto; overflow-y: hidden; scrollbar-width: none; -ms-overflow-style: none; padding: 0 2px; flex: 1; scroll-snap-type: x proximity; }
  #dock-box > .dock.dock-start::-webkit-scrollbar { display: none; }
  #dock-box .dock-key { display: none; }
  #dock-box #start-btn .start-label { display: none; }
  #dock-box #start-btn { padding: 0; width: 44px; justify-content: center; flex: none; }
  #dock-box .dock-btn, #dock-box .dock-btn.pin, #dock-box #taskbar > a { width: 44px; height: 44px; flex: none; scroll-snap-align: start; }
  #dock-box .dock-btn > img, #dock-box .dock-btn.pin > img { width: 40px; height: 40px; }
  #dock-box .dock-divider { flex: none; height: 26px; margin: 0 3px; }
  /* full-screen sheets sit below the Halo and above the dock, and take the
     plan 4.2 sheet radius */
  .window.window { top: 62px !important; height: calc(100% - 62px - 84px) !important; border-radius: var(--r-window) var(--r-window) 0 0 !important; }
  .window.window.compact { height: calc(100% - 62px - 84px) !important; }
  /* The caption used to be inset to 84 and 20 here to keep it clear of the
     window buttons. desktop.css section 3 supersedes it with
     .window.window > .titbar > p at left 0 right 0, padding 0 8px and
     centred, on a measurement: the longest French window name is 121px
     against 136px of clear space to the left of centre. That selector
     carries one more class than this one, so this line has been dead for
     a round. Removed rather than left to be found and trusted. */
  /* Round 11: the phone lock is designed, not squeezed. 16 px margins, the
     clock above the card in the column, the partner strip BELOW the card as
     a row of its own rather than hidden, and the bottom padding leaves the
     EN and FR pills their own row at the foot. Measured at 390 by 844:
     clock 58 to 145, card 168 to 713, strip under the cue, pills at 772. */
  #loginback { padding: 16px 16px 76px; justify-content: center; overflow-y: auto; }
  #loginback > .lock-core { width: 100%; overflow: visible; }
  /* The badge row used to sit where consent lives. Phone height cannot
     keep the carousel, the guest door and the local human check. */
  #loginback .pk-marquee { display: none; }
  #loginback .lock-bot { width: 44px; height: 44px; margin: 0 auto 6px; }
  #loginback > .lock-core > .name { font-size: 20px; }
  #loginback .pk-cap { margin: 8px 0 12px; padding: 0 16px 8px; }
  #login-badge {
    display: block; position: static; right: auto; bottom: auto; left: auto;
    transform: none; margin: 12px auto 0; max-width: min(100%, 358px); max-height: 40px;
    padding: 7px 11px;
  }
  .lock-site { display: none; }
  .os-note { width: min(232px, calc(100vw - 28px)); }
}

@media (prefers-reduced-motion: reduce) {
  .window, #dock-box .dock-btn, #dock-box .dock-key, #os-halo, #os-snap,
  #dock-box #taskbar > a, .os-note { transition: none; }
}

/* ===== the Setup Assistant: depth 3 ===== */
#os-setup { position: fixed; inset: 0; z-index: 100; display: grid; place-items: center; background: rgba(6, 5, 10, 0.52); backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); animation: os-fade 260ms var(--pm-ease); }
#os-setup.out { opacity: 0; transition: opacity 260ms var(--pm-ease); }
@keyframes os-fade { from { opacity: 0; } }
#os-setup .card { width: min(520px, calc(100% - 28px)); padding: 26px 26px 20px; border-radius: var(--os-r-sheet); background: var(--g3-bg); border: var(--g3-border); backdrop-filter: blur(var(--g3-blur)) saturate(var(--g3-sat)); -webkit-backdrop-filter: blur(var(--g3-blur)) saturate(var(--g3-sat)); box-shadow: var(--g3-shadow), inset 0 1px 0 rgba(255, 255, 255, 0.16); text-align: center; animation: os-pop 320ms var(--pm-ease); }
@keyframes os-pop { from { transform: translateY(10px) scale(0.98); opacity: 0; } }
#os-setup .k { font-family: var(--os-mono); font-size: 10.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--os-text-2); }
#os-setup h2 { margin: 6px 0 18px; font-family: var(--os-ui); font-size: 22px; font-weight: 700; letter-spacing: -0.02em; color: var(--os-text); }
#os-setup .chips { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; }
#os-setup .chips button { font-family: var(--os-ui); font-size: 14px; font-weight: 600; color: var(--os-text); background: rgba(255, 255, 255, 0.08); border: 1px solid var(--os-line-2); border-radius: var(--r-control, 10px); padding: 10px 18px; cursor: pointer; transition: background 180ms var(--pm-ease), transform 180ms var(--pm-ease); }
#os-setup .chips button:hover { background: var(--pm-purple); border-color: transparent; color: #fff; transform: translateY(-1px); }
#os-setup .chips button:focus-visible { background: var(--pm-purple); border-color: transparent; color: #fff; }
#os-setup .skip { margin-top: 16px; background: none; border: 0; color: var(--os-text-2); font-family: var(--os-ui); font-size: 12.5px; text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }

/* ===== sheets and dialogs, plan 4.2: 12 px, not the 10 px app window =====
   The Software Update window and the Found Score window are sheets: they
   are a system talking, not an app the visitor keeps open. */
.window.update, .window.diag {
  border-radius: var(--os-r-sheet) !important;
}
.window.update > .titbar, .window.diag > .titbar {
  border-radius: var(--os-r-sheet) var(--os-r-sheet) 0 0;
}
.window.update.max, .window.diag.max { border-radius: 0 !important; }

/* ===== the Found Score window ===== */
.window.diag { width: min(480px, 94%); height: min(640px, 88%); }
.window.diag.compact { height: min(500px, 88%); }
#win-diag { padding: 14px 16px 12px; overflow: auto; font-family: var(--os-body); color: var(--os-text); display: flex; flex-direction: column; gap: 6px; }
#win-diag .dg-ask h3 { font-family: var(--os-ui); font-size: 18px; margin: 20px 0 12px; }
#win-diag .dg-form { display: flex; gap: 8px; }
#win-diag .dg-form input { flex: 1; height: 42px; border-radius: var(--os-r-cta); background: rgba(255, 255, 255, 0.08); border: 1px solid var(--os-line-2); color: var(--os-text); font-family: var(--os-body); font-size: 14px; padding: 0 12px; min-width: 0; }
#win-diag .dg-form input::placeholder { color: var(--os-text-3); }
#win-diag .dg-form input:focus-visible { outline: 2px solid var(--os-focus); border-color: transparent; }
#win-diag .btn { display: inline-flex; align-items: center; gap: 8px; height: 38px; padding: 0 16px; border-radius: var(--os-r-cta); font-family: var(--os-ui); font-weight: 700; font-size: 13px; color: #fff; background: var(--pm-purple); border: 0; cursor: pointer; }
#win-diag .btn:hover { background: var(--pm-purple-deep); color: #14101C; }
#win-diag .btn.ghost { background: rgba(255, 255, 255, 0.08); color: var(--os-text); border: 1px solid var(--os-line-2); }
#win-diag .dg-wait { display: grid; place-items: center; gap: 14px; padding: 46px 10px; text-align: center; color: var(--os-text-2); font-size: 13.5px; line-height: 1.5; }
#win-diag .dg-wait b { color: var(--os-text); font-family: var(--os-ui); }
#win-diag .dg-spin { width: 34px; height: 34px; border-radius: 50%; border: 2px solid var(--os-line-2); border-top-color: var(--pm-lavender); animation: dg-spin 0.9s linear infinite; }
@keyframes dg-spin { to { transform: rotate(360deg); } }
#win-diag .dg-score { display: flex; align-items: center; gap: 16px; padding: 4px 0 6px; }
#win-diag .dg-ring { width: 88px; height: 88px; position: relative; flex: none; }
#win-diag .dg-ring svg { width: 88px; height: 88px; transform: rotate(-90deg); }
#win-diag .dg-ring b { position: absolute; inset: 0; display: grid; place-items: center; font-family: var(--os-ui); font-size: 28px; font-weight: 700; letter-spacing: -0.03em; }
#win-diag .dg-score p { margin: 0; font-size: 12.5px; color: var(--os-text-2); line-height: 1.45; }
#win-diag .dg-score p b { color: var(--os-text); font-family: var(--os-ui); font-size: 13.5px; display: block; margin-bottom: 2px; }
#win-diag .dg-h { font-family: var(--os-mono); font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--os-text-2); padding: 10px 0 2px; }
#win-diag .row { display: flex; align-items: center; gap: 12px; padding: 9px 0; border-bottom: 1px solid var(--os-line); }
#win-diag .row .d { width: 7px; height: 7px; border-radius: 50%; background: var(--os-good); flex: none; }
#win-diag .row .d.warn { background: var(--os-warn); } #win-diag .row .d.bad { background: var(--os-bad); }
#win-diag .row b { font-family: var(--os-ui); font-weight: 600; font-size: 13px; }
#win-diag .row .sub { display: block; color: var(--os-text-2); font-size: 12px; margin-top: 2px; }
#win-diag .row .m { margin-left: auto; font-family: var(--os-mono); font-size: 11px; color: var(--os-text-2); white-space: nowrap; }
#win-diag .dg-list { position: relative; }
#win-diag .dg-list.blur .row { filter: blur(6px); pointer-events: none; user-select: none; }
#win-diag .dg-gate { position: absolute; inset: 0; display: grid; place-items: center; align-content: center; gap: 10px; text-align: center; }
#win-diag .dg-gate p { margin: 0; font-family: var(--os-ui); font-weight: 700; font-size: 15px; }
#win-diag .dg-queue { display: flex; flex-direction: column; gap: 8px; padding: 10px 0 0; font-size: 13.5px; line-height: 1.5; color: var(--os-text-2); }
#win-diag .dg-queue > p { margin: 0; }
#win-diag .dg-queue .dg-h { color: var(--os-text); font-family: var(--os-ui); font-size: 15px; font-weight: 700; letter-spacing: 0; text-transform: none; padding: 0; }
#win-diag .dg-k { font-family: var(--os-mono); font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--os-text-2); padding: 8px 0 0; }
#win-diag .dg-queue ul { margin: 0; padding: 0; list-style: none; display: grid; grid-template-columns: 1fr 1fr; gap: 6px 12px; }
#win-diag .dg-queue li { position: relative; padding-left: 16px; font-size: 13px; color: var(--os-text); }
#win-diag .dg-queue li::before { content: ""; position: absolute; left: 0; top: 8px; width: 7px; height: 7px; border-radius: 50%; background: var(--pm-lavender); }
#win-diag .dg-ok { margin: 6px 0 0; padding: 10px 12px; border-radius: var(--os-r-cta); background: rgba(110, 90, 232, 0.22); color: var(--os-text); font-family: var(--os-ui); font-weight: 600; font-size: 13px; }
#win-diag .dg-queue-form { margin-top: 6px; }
#win-diag .dg-ask .dg-checks { margin: 0; padding: 0; list-style: none; display: grid; grid-template-columns: 1fr 1fr; gap: 6px 12px; }
#win-diag .dg-ask .dg-checks li { position: relative; padding-left: 16px; font-size: 13px; color: var(--os-text); }
#win-diag .dg-ask .dg-checks li::before { content: ""; position: absolute; left: 0; top: 8px; width: 7px; height: 7px; border-radius: 50%; background: var(--pm-lavender); }
#win-diag .dg-ask .dg-k { padding-top: 22px; }
#win-diag .dg-by { margin: 14px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--os-text-2); }
#win-diag .dg-foot { display: flex; align-items: center; gap: 12px; padding: 12px 0 0; margin-top: auto; border-top: 1px solid var(--os-line); font-size: 12px; color: var(--os-text-2); }
#win-diag .dg-foot .sp { flex: 1; }

/* ###########################################################################
   ROUND 6 WAVE 4: THE DEPTH LAYER
   ###########################################################################

   Everything below this fence is the depth layer and nothing above it was
   touched. It is fenced so a merge with another agent working in this file
   is a conflict in one region rather than scattered through 800 lines.

   WHY THIS BLOCK EXISTS. Two independent design reviews, on two different
   days, both said the same thing about the running build: there is no
   glass, the window casts essentially no shadow, everything is on one
   z plane, the composition reads as stacked stickers.

   Both reviews were right about what they saw and wrong about why. The
   glass and the shadow were declared correctly. They were being FLATTENED.

   THE ROOT CAUSE, measured rather than guessed. pekan/clutter.css declares
   a dialog scrim, .clut-back, at position fixed, inset 0, z-index 999992,
   background rgba(8, 6, 14, 0.52), with no backdrop blur. On a plain visit
   to /desktop.html?boot=0 that sheet is on screen after the guest unlock
   and it stays there. It sits above the wallpaper, the windows, the dock
   and the menu bar, and it multiplies the entire composition by about 0.48
   in a single pass.

   That is what kills the depth. A scrim darkens the wallpaper AND the
   shadow on it by the same factor, so the RATIO between them collapses,
   and ratio is the only thing the eye reads as depth. Measured, same
   window, same wallpaper, same instant, as a delta against the identical
   ground with the window hidden:

     with the scrim on screen   contact 1.82 of 255, ambient 1.02, dead by y+95
     with the scrim removed     contact 24.4 of 255, ambient 12,    reaches y+95

   The shadow is thirteen times stronger the moment that sheet is gone.
   Every screenshot in both reviews was taken through it.

   The permanent home for that fix is pekan/clutter.css, which this agent
   does not own, so it is written up in fleet/requests/depth.md with the
   exact selector and rule. What is done HERE is the part that is genuinely
   a depth concern: a scrim that stops the OS is a depth object, so it is
   given the depth system's own scrim treatment (blur plus a lower alpha)
   rather than being left as a flat black sheet.

   THE SECOND CAUSE. An app pane paints opaque over the glass from the
   inside. #win-taskmgr > .main is rgb(20, 17, 28) at alpha 1.0 covering 61
   percent of the window, so a window with correct glass at its edges still
   reads as a flat plate across most of its face. The elevation ramp below
   is therefore TRANSLUCENT: the steps compose over the glass instead of
   replacing it.

   THE LIGHT MODEL. One sun, high and slightly forward, declared once here
   and obeyed by every shadow in the OS. Every drop shadow has a positive Y
   and zero X. Every lit edge is the TOP edge, drawn as an inset highlight.
   Every floating surface casts TWO layers: a tight key shadow that says
   where the object touches, and a wide low alpha ambient that says how far
   it floats. Nothing that floats casts only one.

   No em dashes anywhere in this block. */

:root {
  /* the one light direction, declared once */
  --os-light-x: 0;                 /* no horizontal offset: the sun is high */
  --os-light-y: 1;                 /* shadows fall down, always */

  /* the two layer shadow scale
     Each step is key plus ambient. The ambient blur roughly doubles between
     steps while the key grows slowly, because that ratio is what reads as
     height rather than as darkness. The ambient alpha FALLS as the surface
     rises, which is counterintuitive but correct: a high object throws a
     softer, wider, fainter shadow, not a darker one. */

  /* The outer dark hairline. A dark surface on a dark wallpaper has no edge
     without it. Drawn as a shadow, not a border, so it never eats layout.

     NAMED --os-edge, NOT --os-hair. pekan/authenticity.css already owns
     --os-hair as a LENGTH (1px, and 0.5px or 0.34px on high density
     screens) for its inset hairline rules. A token named --os-hair holding
     a full shadow value would resolve to "1px" inside this shorthand, which
     is not a valid shadow, and CSS discards the ENTIRE declaration when one
     component is invalid. That is not a theory: it shipped for one build of
     this block and every modal, menu and toast in the OS silently lost its
     whole shadow, computed box-shadow "none", while the stylesheet looked
     correct. Measured and caught. Do not reuse another sheet's token name
     for a different type. */
  --os-edge: var(--glass-edge);
  --os-edge-soft: 0 0 0 1px rgba(0, 0, 0, 0.42);

  /* Round 11: the depth scale is the glass recipe's shadow set, aliased.
     depth 1.5 rests on the desk, 2 floats, 2f is the focused window, 3 is
     lifted clear, 4 is modal. Same names as before, one source. */
  --os-sh-rest: var(--glass-shadow-rest);
  --os-sh-2: var(--glass-shadow);
  --os-sh-2f: var(--glass-shadow-focus);
  --os-sh-3: var(--glass-shadow-lift);
  --os-sh-4: var(--glass-shadow-modal);

  /* the inner light stroke: the top edge catching the sun, aliased onto
     the recipe. Plan 4.3 gives the dock value at white 22 percent. */
  --os-rim-1: var(--glass-rim-1);
  --os-rim-2: var(--glass-rim-2);
  --os-rim-3: var(--glass-rim-3);
  --os-rim-4: var(--glass-rim-4);

  /* the modal scrim, as tokens so every scrim in the OS agrees
     A scrim BLURS what is behind it. A scrim that only darkens is a layer
     slider, which is the exact defect the review named. The alpha is low
     because the blur is doing the separation, not the black. */
  --os-scrim-bg: rgba(8, 6, 14, 0.42);
  --os-scrim-blur: 10px;

  /* The elevation ramp, TRANSLUCENT so it composes over glass.

     The review found sidebar, header and content within a few points of
     lightness of each other, so there was no elevation at all. These are
     real steps, but they are white overlays at rising alpha rather than
     opaque plates, so a window keeps reading as glass while its internal
     surfaces genuinely separate. Each step is about 3 points of lightness
     over the depth 2 composite, which is the smallest move that survives a
     20px blur and a drifting wallpaper.

     NAMING. These are os- prefixed rather than the bare --el-N that
     pekan/craft.css also declares. w17 reached the same conclusion in the
     same round and their sheet loads after this one, so bare --el-N names
     would silently take their values and this file would be describing a
     ramp it does not control. The depth layer therefore owns its own names
     and the contrast sampler reads THESE, so the measured floor is a
     statement about the values this file actually sets. */
  --os-el-0: rgba(0, 0, 0, 0.22);          /* sunk: a well, a table ground */
  --os-el-1: rgba(255, 255, 255, 0.022);   /* base: the content area */
  --os-el-2: rgba(255, 255, 255, 0.055);   /* raised: header, sidebar, tabs */
  --os-el-3: rgba(255, 255, 255, 0.085);   /* hovered row, active tab */
  --os-el-4: rgba(255, 255, 255, 0.125);   /* selected row, pressed control */

  /* the state set, declared once for every interactive thing
     The review found one field with a focus ring and the next with none,
     and a primary button with no hover, active or disabled design. These
     five values are the whole state model and every control below reads
     them rather than inventing its own. */
  --st-hover: rgba(255, 255, 255, 0.09);
  --st-active: rgba(255, 255, 255, 0.14);
  --st-sel: rgba(255, 255, 255, 0.10);   /* selection is NEUTRAL, not accent */
  --st-sel-line: rgba(255, 255, 255, 0.22);
  --st-disabled-o: 0.42;

  /* the accent is reserved for ONE job: the primary action. Selection,
     tile glow and window rails are neutral, so the accent means something
     when it does appear. This is the whole answer to monochrome fatigue. */
  --os-accent: var(--pm-purple);
  --os-accent-hi: var(--pm-purple-deep);
  --os-accent-press: #5A48C8;
}

/* Under a reduced transparency preference the depth system stays, because
   shadows are not the expensive part, but the blur goes and the scrim
   becomes an honest opaque sheet. The shadows tighten too: a flat surface
   carrying a huge ambient shadow reads as a sticker. */
@media (prefers-reduced-transparency: reduce) {
  :root {
    --os-scrim-bg: rgba(8, 6, 14, 0.82);
    --os-scrim-blur: 0px;
    --os-sh-2: 0 2px 6px rgba(0, 0, 0, 0.44), 0 12px 28px rgba(0, 0, 0, 0.36);
    --os-sh-2f: 0 3px 9px rgba(0, 0, 0, 0.48), 0 20px 46px rgba(0, 0, 0, 0.44);
    --os-sh-3: 0 4px 11px rgba(0, 0, 0, 0.50), 0 24px 54px rgba(0, 0, 0, 0.46);
    --os-sh-4: 0 6px 16px rgba(0, 0, 0, 0.54), 0 32px 72px rgba(0, 0, 0, 0.50);
    /* with no blur under them the steps have to work a little harder to
       separate, because there is no bokeh gradient helping them */
    --os-el-1: rgba(255, 255, 255, 0.03);
    --os-el-2: rgba(255, 255, 255, 0.07);
    --os-el-3: rgba(255, 255, 255, 0.11);
    --os-el-4: rgba(255, 255, 255, 0.16);
  }
}

/* ===========================================================================
   1. THE FLATTENERS

   Two full viewport sheets darken the whole OS without blurring it. Both
   are given the depth system's scrim treatment. This is the single change
   that makes every shadow in the OS visible, so it comes first.
   =========================================================================== */

/* the desktop object dialog scrim. Owned by pekan/clutter.css; this rule is
   scoped to the two properties that are depth concerns and touches nothing
   else about it, so w17's geometry and layout survive untouched. */
.clut-back {
  background: var(--os-scrim-bg);
  backdrop-filter: blur(var(--os-scrim-blur)) saturate(0.9);
  -webkit-backdrop-filter: blur(var(--os-scrim-blur)) saturate(0.9);
}

/* the system dialog scrim. It had no blur at all, so a dialog opened over a
   full brightness table and the eye had to fight the bright numbers to read
   the headline. */
#notice-back {
  background: var(--os-scrim-bg);
  backdrop-filter: blur(var(--os-scrim-blur)) saturate(0.9);
  -webkit-backdrop-filter: blur(var(--os-scrim-blur)) saturate(0.9);
}

/* the setup assistant is a modal too, so it takes the same scrim rather
   than its own hand rolled one */
#os-setup {
  background: var(--os-scrim-bg);
  backdrop-filter: blur(var(--os-scrim-blur)) saturate(0.9);
  -webkit-backdrop-filter: blur(var(--os-scrim-blur)) saturate(0.9);
}

/* ===========================================================================
   2. LAYER OPACITY IS NEVER THE ANSWER

   The review's key insight and the north star for this whole dispatch:
   glass changes the SURFACE, never the layer opacity. Text on glass stays
   crisp at full opacity; only the panel's background is translucent.

   Measured: exactly one element in the running OS breaks this rule.
   apps/style/notepad.css sets #win-notepad > .text-box { opacity: 0.8 },
   which fades the TEXT and the panel together, so the ink goes weak while
   the panel stays the same value as its surroundings. That is the "layer
   slider" the review described, and it is the opposite of glass.

   The permanent home is that stylesheet, which this agent does not own, so
   it is written into fleet/requests/depth.md. Neutralised here because
   illegible ink is a contrast failure and the contrast floor is this
   dispatch's contract.
   =========================================================================== */
#win-notepad > .text-box {
  /* the LAYER goes back to full strength so the text is crisp */
  opacity: 1;
  /* and the SURFACE carries the translucency instead, which is the whole
     difference between glass and a layer slider. A typing surface is a
     WELL, so it takes the sunk step and the inner shadow that goes with it,
     which is also what separates it from the toolbar above it now that the
     opacity trick is not doing that job by accident. */
  background-color: var(--os-el-0);
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.30);
}

/* ===========================================================================
   3. EVERY FLOATING SURFACE CASTS TWO LAYERS

   An element that floats and does not cast both a contact shadow and a
   wide ambient is a defect. This is the complete list of floating surfaces
   in the OS, each on the one light direction, each with the outer hairline
   that gives it an edge against the wallpaper and the inner light stroke
   that gives it a lit top edge.

   os2031.css above and the later sheets both set some of these with an
   !important, so these answer in kind where they must. Where they do not
   have to, they do not.
   =========================================================================== */

/* windows. A window clips its own overflow at the 10px radius, so the
   frame carries the hairline and the two drop layers, and the lit top edge
   is drawn on the title bar, which IS the window's top edge. */
.window,
:root.mica .window {
  box-shadow: var(--os-edge), var(--os-sh-2) !important;
}

.window.foc,
:root.mica .window.foc {
  box-shadow: var(--os-edge), var(--os-sh-2f) !important;
}

/* a maximised window touches every edge, so it floats above nothing and
   casts nothing. A shadow on it would be a shadow into the bezel. */
.window.max,
:root.mica .window.max {
  box-shadow: none !important;
}

/* the dock, the menu bar and the status pill */
#dock-box {
  box-shadow: var(--os-edge), var(--os-sh-2), var(--os-rim-3);
}

/* the bar spans the screen so it has no left or right edge to light, but it
   still has to separate from the wallpaper underneath it */
#os-menubar {
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.48), 0 10px 30px rgba(0, 0, 0, 0.24), var(--os-rim-2);
}

/* the pill when it floats free (it is seated in the bar at desktop sizes,
   and pekan/authenticity.css correctly clears the doubled blur there) */
#os-halo {
  box-shadow: var(--os-edge), var(--os-sh-3), var(--os-rim-4);
}

/* notes stay paper: they do not take the glass depth recipe */

#start-menu,
#cm,
#dp,
.pk-toast,
#notice,
#os-note-undo,
#open-dock,
.clut-dlg,
.msg.update {
  box-shadow: var(--os-edge), var(--os-sh-3), var(--os-rim-4);
}

/* depth 4: the two things that stop the whole OS */
#loginback > .lock-core,
#os-setup .card {
  box-shadow: var(--os-edge), var(--os-sh-4), var(--os-rim-4);
}

/* ===========================================================================
   4. REAL GLASS

   Four things at once, or it is not glass:
     blur AND saturate, because the saturation lift is what makes a surface
       read as glass rather than as fog,
     a 1px inner light stroke along the TOP edge,
     a 1px outer dark hairline so the panel has a defined edge,
     and a fine noise film over the blur, because a wide blur of a near
       black gradient bands visibly without one.

   The first three are above. This is the fourth, plus the lit top edge on
   the window, which is the one surface that could not carry its own rim
   because it clips.
   =========================================================================== */

/* the window's lit top edge, drawn on the title bar. The bar also has to
   carry the blur, or it reads as an opaque strip laid on a translucent box. */
.window > .titbar,
.window.webapp > .titbar,
.window.webapp.foc > .titbar,
.window.chrome > .titbar {
  box-shadow: var(--os-rim-3);
  backdrop-filter: blur(var(--g2-blur)) saturate(var(--g2-sat));
  -webkit-backdrop-filter: blur(var(--g2-blur)) saturate(var(--g2-sat));
}

/* an inactive window catches less light, the way a real one does */
.window:not(.foc) > .titbar {
  box-shadow: var(--os-rim-1);
}

.window.max > .titbar {
  box-shadow: none;
}

/* the noise film. It is a ::after pinned to the surface at a low alpha,
   drawn with pointer-events none so it can never intercept a click, and
   applied by an explicit list rather than a wildcard so nothing pays for it
   twice. overlay blend keeps it invisible on mid tones and only active in
   the flat dark regions where banding actually happens. */
.window::after,
#dock-box::after,
#start-menu::after,
#cm::after,
.os-note::after,
.pk-toast::after,
#notice::after,
#os-setup .card::after,
#loginback > .lock-core::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  border-radius: inherit;
  opacity: 0.5;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='96' height='96'><filter id='d'><feTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.03 0'/></filter><rect width='100%' height='100%' filter='url(%23d)'/></svg>");
}

/* the film must sit behind the content, never over it */
.window > .titbar,
.window > .content,
.window > .main,
#dock-box > *,
#start-menu > *,
.os-note > *,
#notice > * {
  position: relative;
  z-index: 1;
}

/* a maximised window has no floating edge, so no film and no rim */
.window.max::after { display: none; }

/* ===========================================================================
   5. THE INACTIVE WINDOW, REBUILT

   Plan 4.3: an inactive window keeps the SAME blur at surface alpha 0.50
   with a dimmer border, so the wallpaper reads through it more, while the
   TEXT STAYS LEGIBLE. Never a global opacity, never a filter on the layer.

   pekan/authenticity.css already lands the surface alpha correctly. What it
   also does is filter: saturate(0.9) on the whole window, which is a layer
   effect on the text as well as the panel. It is a mild one, but the rule
   in this dispatch is absolute: the surface changes, the layer never does.
   =========================================================================== */
.window:not(.foc) {
  /* the surface is thinner and greyer. That is the entire inactive
     treatment. Round 11 adds the grey: the backdrop saturation drops from
     the active 1.62 to --glass-sat-idle, which is a change to the SURFACE
     and leaves the ink untouched, so this rule and plan 4.3 still agree. */
  background: var(--g2-bg-idle);
  border-color: var(--g2-border-idle) !important;
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat-idle));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat-idle));
  /* no layer filter and no layer opacity, so the ink is exactly as crisp as
     it is on the active window */
  filter: none;
  opacity: 1;
}

/* the title dims by COLOUR, which is a paint change on one element, rather
   than by opacity, which would be a layer change on all of them */
.window:not(.foc) > .titbar > p {
  color: var(--os-text-2);
  opacity: 1;
}

/* ===========================================================================
   6. THE MODAL STATE MODEL

   The review: "the Task Manager stays fully active (bright title, bright
   dots) while a modal owns focus. That is a state model error, not just a
   paint error."

   When a modal owns focus, every window behind it goes inactive: dots
   dimmed, title dimmed, surface thinned. Keyed off body.os-modal (which
   w13 sets, see fleet/requests/depth.md) and also off the clutter scrim
   being present, so the desktop object dialogs get it with no JS change.
   =========================================================================== */
body.os-modal .window,
body:has(> .clut-back) .window {
  background: var(--g2-bg-idle);
  border-color: var(--g2-border-idle) !important;
  box-shadow: var(--os-edge), var(--os-sh-2) !important;
}

body.os-modal .window > .titbar > p,
body:has(> .clut-back) .window > .titbar > p {
  color: var(--os-text-2);
}

body.os-modal .window > .titbar,
body:has(> .clut-back) .window > .titbar {
  box-shadow: var(--os-rim-1);
}

/* the dots go to their inactive values, so the window visibly stops being
   the one that is listening */
body.os-modal .window > .titbar .wbtg.red,
body:has(> .clut-back) .window > .titbar .wbtg.red {
  background: rgba(110, 90, 232, 0.50);
}

body.os-modal .window > .titbar .wbtg:not(.red):not(.max),
body:has(> .clut-back) .window > .titbar .wbtg:not(.red):not(.max) {
  background: rgba(228, 197, 106, 0.44);
}

body.os-modal .window > .titbar .wbtg.max,
body:has(> .clut-back) .window > .titbar .wbtg.max {
  background: rgba(62, 156, 156, 0.44);
}

/* the modal itself is depth 4: it is above the scrim, so it must cast the
   biggest shadow in the system or it will look pasted onto the blur */
.clut-dlg,
body.os-modal #notice {
  box-shadow: var(--os-edge), var(--os-sh-4), var(--os-rim-4);
}

/* ===========================================================================
   7. THE ELEVATION HIERARCHY, APPLIED

   wallpaper, then window base, then raised surface, then floating surface,
   then modal. Each step visibly distinct. The steps are translucent so the
   window keeps reading as glass, which is the fix for the opaque app pane
   described at the top of this block.
   =========================================================================== */

   WHO OWNS WHAT. pekan/craft.css (w17) landed the Task Manager's own ramp
   in the same round and reached the same conclusion this file did, that
   elevation inside a translucent window has to be built from translucent
   steps or the opaque child cancels the window's blur. Their sheet loads
   after this one and their selectors are more specific, so THEY own the
   Task Manager interior and there is no reason for this file to restate it.
   Duplicating it would only guarantee a conflict.

   What the depth layer owns is the part that is not app specific: the ramp
   as a token set any surface can read, the generic well, and the rule that
   a selected row is neutral rather than accent. */

/* a sunk well: a field, a table ground, a code block. This is the bottom of
   the ramp and it is the one step that goes DARKER rather than lighter,
   because a well is a hole in the surface, not a plate on it. */
.os-well {
  background: var(--os-el-0);
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.30);
}

/* a raised surface: a header, a sidebar, a tab strip. */
.os-raised {
  background: var(--os-el-2);
}

/* Selection anywhere in the OS is NEUTRAL, not accent. Selection is not a
   call to action, so it does not get the accent colour: it gets the top of
   the neutral ramp plus a left rail, which is also the non colour cue that
   keeps it legible for anyone who cannot separate the two hues. */
.window tbody > tr.sel,
.window tbody > tr.select,
.window list > .sel,
.window .row.sel {
  background: var(--os-el-4);
  box-shadow: inset 2px 0 0 var(--os-accent);
}

/* ===========================================================================
   8. FOCUS AND STATE, THE FULL SET

   The review: one field has a plum focus ring and the next has none, the
   two fills are identical so the ring does all the work, the primary button
   has no visible hover, active or disabled design, and Privacy is a link
   distinguished by COLOUR ALONE.

   Built once here as rest, hover, active, focus visible and disabled, for
   buttons, links, fields and dock items.
   =========================================================================== */

/* the primary button: four visible states */
.btn,
.button,
#win-diag .btn,
.pk-toast button,
.pk-toast .primary,
#os-note-undo button {
  background: var(--os-accent);
  color: #fff;
  border: 0;
  /* a button is a raised object, so it casts a small shadow at rest */
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.34), 0 3px 8px rgba(0, 0, 0, 0.22), var(--os-rim-2);
  transition: background 150ms var(--pm-ease), box-shadow 150ms var(--pm-ease), transform 120ms var(--pm-ease);
}

.btn:hover,
.button:hover,
#win-diag .btn:hover,
.pk-toast button:hover,
#os-note-undo button:hover {
  background: var(--os-accent-hi);
  color: #14101C;
  /* hover lifts: more shadow, not just more colour */
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.36), 0 6px 16px rgba(0, 0, 0, 0.28), var(--os-rim-3);
}

.btn:active,
.button:active,
#win-diag .btn:active,
.pk-toast button:active,
#os-note-undo button:active {
  background: var(--os-accent-press);
  color: #fff;
  /* active presses IN: the shadow collapses and the rim goes out, which is
     the physical opposite of hover */
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.40);
  transform: translateY(1px);
}

/* THE FOCUS RING ON A BUTTON.

   Measured defect, caught by driving the real control rather than reading
   the stylesheet: the global :focus-visible rule at the top of this file
   draws the ring with an outline AND a box-shadow halo, but the button
   rules above set box-shadow for the resting lift. A later, equally
   specific box-shadow wins, so the halo was being overwritten and the ring
   lost the dark surround that makes it clear 3:1 on a drifting wallpaper.

   The fix is to restate the whole shadow stack on focus: the halo first,
   then the button's own lift, so the control keeps its elevation while the
   ring keeps its guarantee. */
.btn:focus-visible,
.button:focus-visible,
#win-diag .btn:focus-visible,
.pk-toast button:focus-visible,
#os-note-undo button:focus-visible {
  outline: 2px solid var(--os-focus);
  outline-offset: 2px;
  box-shadow: 0 0 0 6px var(--os-focus-halo),
              0 1px 2px rgba(0, 0, 0, 0.34), 0 3px 8px rgba(0, 0, 0, 0.22);
}

.btn:disabled,
.button:disabled,
.btn[aria-disabled="true"],
.button[aria-disabled="true"],
#win-diag .btn:disabled {
  /* a disabled control is FLAT: no lift, no rim, nothing to press. The
     opacity here is on a leaf control with no text of its own to protect,
     which is the one place a layer fade is honest, and it is paired with a
     background change so the state does not rely on the fade alone. */
  opacity: var(--st-disabled-o);
  box-shadow: none;
  cursor: not-allowed;
  background: var(--st-active);
  color: var(--os-text-2);
}

/* the ghost button is the same state machine on a neutral fill */
#win-diag .btn.ghost {
  background: var(--st-hover);
  color: var(--os-text);
  border: 1px solid var(--os-line-2);
  box-shadow: none;
}

#win-diag .btn.ghost:hover {
  background: var(--st-active);
  border-color: var(--os-line-3);
}

#win-diag .btn.ghost:active {
  background: var(--st-hover);
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.36);
  transform: translateY(1px);
}

/* links need a NON COLOUR affordance. Colour alone fails WCAG 1.4.1. */
.window a:not(.btn):not(.button):not(.a):not([role="button"]),
.pk-toast a,
#notice a,
.lock-consent a,
#win-diag a,
.lock-why a {
  color: var(--pm-lavender);
  /* the underline IS the affordance. It is always on, not on hover. */
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: rgba(169, 155, 255, 0.55);
  transition: text-decoration-color 140ms var(--pm-ease), color 140ms var(--pm-ease);
}

.window a:not(.btn):not(.button):not(.a):not([role="button"]):hover,
.pk-toast a:hover,
#notice a:hover,
.lock-consent a:hover,
#win-diag a:hover,
.lock-why a:hover {
  color: #C9BEFF;
  text-decoration-color: #C9BEFF;
  text-decoration-thickness: 2px;
}

/* fields: one fill, one border, one focus ring, every field */
.input,
input.input,
#win-diag .dg-form input,
.pk-toast input,
.window input[type="text"],
.window input[type="email"],
.window input[type="tel"],
.window input[type="search"],
.window textarea {
  background: var(--os-el-0);
  border: 1px solid var(--os-line-2);
  color: var(--os-text);
  /* a field is SUNK, so it takes an inner shadow rather than a drop shadow.
     That is what makes it read as a well you type into rather than a button. */
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.30);
  transition: border-color 140ms var(--pm-ease), box-shadow 140ms var(--pm-ease), background 140ms var(--pm-ease);
}

.input:hover,
input.input:hover,
#win-diag .dg-form input:hover,
.window input[type="text"]:hover,
.window input[type="email"]:hover,
.window input[type="tel"]:hover,
.window textarea:hover {
  border-color: var(--os-line-3);
}

/* every field gets the ring, and the ring is the same ring everywhere.
   Plan 4.1: #A99BFF, 2px, offset 2. The dark halo outside it is what makes
   it clear 3:1 against a drifting wallpaper, per WCAG 1.4.11. */
.input:focus-visible,
input.input:focus-visible,
#win-diag .dg-form input:focus-visible,
.pk-toast input:focus-visible,
.window input:focus-visible,
.window textarea:focus-visible {
  outline: 2px solid var(--os-focus);
  outline-offset: 2px;
  box-shadow: 0 0 0 6px var(--os-focus-halo), inset 0 1px 2px rgba(0, 0, 0, 0.30);
  border-color: var(--os-focus);
  background: var(--os-el-0);
}

.input:disabled,
input.input:disabled,
.window input:disabled,
.window textarea:disabled {
  opacity: var(--st-disabled-o);
  cursor: not-allowed;
  box-shadow: none;
}

/* DOCK ITEMS: the same five states as everything else.

   Measured defect: pressing a dock tile produced the same pixels as
   hovering it, and tabbing to one produced no ring at all, because
   os2031.css above puts :focus-visible in the same rule as :hover and then
   sets background: none on both. A control where press and hover are
   identical does not feel like a control.

   These carry an important because the rule they answer is a compound
   selector earlier in this same file that also sets background: none, and
   equal specificity plus later position is not enough when the earlier rule
   is more specific by one class. */
#dock-box .dock-btn:active,
#dock-box .dock-btn.pin:active,
#dock-box #taskbar > a:active,
#dock-box .dock-key:active,
#dock-box #start-btn:active {
  /* press goes DOWN and IN: the tile settles toward the dock surface,
     which is the physical opposite of the hover lift */
  background-color: var(--st-active) !important;
  transform: scale(0.96) !important;
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.34);
}

/* the ring has to survive the background: none that the hover rule sets */
#dock-box .dock-btn:focus-visible,
#dock-box .dock-btn.pin:focus-visible,
#dock-box #taskbar > a:focus-visible,
#dock-box .dock-key:focus-visible,
#dock-box #start-btn:focus-visible {
  outline: 2px solid var(--os-focus);
  outline-offset: 2px;
  box-shadow: 0 0 0 6px var(--os-focus-halo);
  background-color: var(--st-hover) !important;
}

#dock-box .dock-btn[aria-disabled="true"],
#dock-box .dock-btn:disabled {
  opacity: var(--st-disabled-o);
  cursor: not-allowed;
  transform: none !important;
}

/* ===========================================================================
   9. MONOCHROME FATIGUE

   The review: about three purples do everything, so nothing has priority,
   and every desktop tile carries a soft purple glow that reads as a hover
   state stuck permanently on.

   The hierarchy is: ONE accent for the primary action, a quieter NEUTRAL
   for selection, and no permanent glow anywhere.
   =========================================================================== */

/* selection on the desktop goes neutral. It was rgba(110, 90, 232, 0.26)
   with a lavender outline, which is the accent doing a job that is not a
   call to action. A selected icon is not asking to be clicked; it is
   reporting that it already was. */
#desktop > div.sel,
#desktop.clut-free > div.b.clut.sel {
  background: var(--st-sel);
  outline: 1px solid var(--st-sel-line);
  box-shadow: var(--os-sh-rest);
}

/* hover is quieter than selection, so the two are distinguishable */
#desktop > div:hover {
  background: var(--st-hover);
}

/* the marquee stays on the accent, because a drag rectangle is a live
   action rather than a resting state */
#desktop > .selection {
  border: 1px solid var(--os-accent);
  background: rgba(110, 90, 232, 0.16);
}

/* text selection goes neutral for the same reason */
::selection {
  background: rgba(255, 255, 255, 0.22);
  color: #FFFFFF;
}

/* no permanent glow on a tile. The icon keeps its CONTACT shadow, because
   that is physics and it is what puts the object on the desk, but nothing
   carries a coloured halo at rest. */
#desktop > div,
#desktop > div > img,
#desktop > div.b.clut > img {
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.50)) drop-shadow(0 3px 8px rgba(0, 0, 0, 0.26));
}

#desktop > div:hover > img,
#desktop > div.b.clut:hover > img {
  /* hover MEANS something now: the object lifts off the desk, which is a
     longer shadow, not a colour change */
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.50)) drop-shadow(0 6px 14px rgba(0, 0, 0, 0.30));
}

/* ===========================================================================
   10. PERFORMANCE

   backdrop-filter is expensive. Two rules keep it affordable:
     blur is never animated or transitioned, on scroll or anywhere else,
     and a window being dragged drops its blur entirely, because a moving
       surface invalidates the blur every single frame and nobody can see a
       20px blur on something in motion anyway.

   NOT DONE HERE: contain: paint. An earlier draft of this block put
   contain: paint on every blurred surface as a compositor hint. Measured,
   it silently DESTROYED the backdrop filter's saturation. With contain on,
   saturate(1), saturate(1.35) and saturate(2.8) all produced identical
   pixels: chroma 15.56, 15.56, 15.60. With contain removed, the same three
   values produced chroma 12.50, 15.56 and 30.04, which is the effect
   actually working. The reason is that contain: paint establishes a
   containing block that clips what the backdrop filter is allowed to
   sample, so the filter runs against a flattened local backdrop rather
   than the real one behind it.

   It cost nothing either: removing it moved the median frame from 83.4 ms
   to 75.5 ms, so the "optimisation" was a net loss on both axes. It is
   documented rather than deleted so nobody adds it back as an obvious win.
   =========================================================================== */

/* blur is never in a transition, at any depth, ever */
.window,
.window.foc,
.window:not(.foc),
#dock-box,
#os-menubar,
.os-note,
#start-menu,
#cm,
#notice,
.clut-dlg {
  transition-property: transform, opacity, background-color, border-color, box-shadow;
}

/* a window in motion pays nothing for a blur it cannot show */
.window.drag,
.window.sizing {
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  /* the surface goes opaque enough to stand alone for the duration, so the
     content does not flash through while the blur is off */
  background: rgba(var(--g2-rgb), 0.90) !important;
}
.os-note.drag,
.os-note.sizing {
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  background: var(--note-bg, #F4D35E) !important;
}

@media (prefers-reduced-motion: reduce) {
  .btn, .button, #win-diag .btn, .pk-toast button, #os-note-undo button,
  .input, input.input, #login-phone,
  #desktop > div, #desktop > div > img,
  .window a, .pk-toast a, #notice a {
    transition: none;
  }
}

/* The label that sits above any panel of invented figures. Used by the
   Security Centre and the Task Manager performance page. It had no rule at
   all, so it rendered at body weight and read like a caption nobody wrote on
   purpose. It should be quiet and legible: a visitor who wants to know whether
   a number is real must be able to find it, and a visitor who does not should
   not have it shouting. Contrast is the OS secondary text on the OS ground,
   which measures well past AA. */
.pk-illustrative {
  margin: 0 0 10px;
  font-family: var(--os-ui, inherit);
  font-size: 12px;
  line-height: 1.45;
  letter-spacing: 0.01em;
  color: var(--os-text-2, #c4c0d0);
  opacity: 0.92;
}

/* Empty states. A window that opens with nothing in it reads as broken even
   when it is working exactly as designed, so the two that can legitimately
   open with no content say what they are waiting for. */
#win-mediaplayer .mp-empty,
#win-run .run-hint {
  margin: 0;
  padding: 18px 20px;
  font-family: var(--os-ui, inherit);
  font-size: 13px;
  line-height: 1.5;
  color: var(--os-text-2, #c4c0d0);
}
#win-run .run-hint { padding: 0 0 10px; }
