/*
 * Shared by the home page and every app page.
 *
 * This was inline in index.html when the site was one page, on the argument that over three Tor
 * relays a round trip costs more than the bytes. With thirteen pages that argument inverts: an
 * inline copy is re-downloaded on every navigation and thirteen copies drift apart, whereas one
 * file costs a single extra round trip on first load and is cached for every page after it.
 */
  :root {
    /* The app palette, lifted from packages/core/src/theme/theme.ts so the site and the
       products cannot drift apart. */
    --bg: #1c232b;
    --surface: #242c35;
    --elevated: #2e3742;
    --text: #ffffff;
    --muted: #c9d0d8;
    --faint: rgba(255, 255, 255, 0.62);
    --brand: #f4c430;   /* the Officer yellow */
    --ink: #0e7490;     /* the teal that pairs with it */
    --line: rgba(255, 255, 255, 0.14);
    /* The only colour outside the brand palette, and it earns its place: the ladder needs a top
       rung that reads as heavier than the two below it, and yellow cannot say that on a page where
       yellow already means "this is ours". */
    --alarm: #e5675c;
  }

  * { box-sizing: border-box; }

  /* CSS, not JavaScript — so it still works at Tor Browser's Safest level, where scripts are off.
     The anchor below degrades to an ordinary jump if a browser does not support it, which is a
     working link either way. */
  html { scroll-behavior: smooth; }

  /* Anyone who has asked the OS for less motion gets the instant jump instead. */
  @media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
  }

  body {
    margin: 0;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    background: var(--bg);
    color: var(--text);
    font: 16px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
  }

  .wrap { max-width: 1200px; margin: 0 auto; padding: 0 32px; }

  /* Child selector, not `.wrap` — the footer has a .wrap of its own and must not grow. */
  body > .wrap { flex: 1 0 auto; width: 100%; }

  /* ── hero ─────────────────────────────────────────────────────────────────── */

  header {
    padding: 32px 0 56px;
    display: flex;
    align-items: stretch;
    justify-content: center;
    gap: 28px;
  }

  /* `align-self: stretch` + `object-fit: contain` is what actually makes the logo the same height
     as the text: the row's height comes from the text block, the image fills it, and contain keeps
     the duck square rather than distorting it. A fixed px height would drift the moment the
     headline wrapped differently. */
  .duck {
    align-self: stretch;
    width: auto;
    height: auto;
    max-height: 180px;
    object-fit: contain;
    border-radius: 22px;
    flex: 0 0 auto;
  }

  .hero-text { display: flex; flex-direction: column; justify-content: center; }

  h1 {
    margin: 0 0 10px;
    font-size: 44px;
    line-height: 1.05;
    letter-spacing: -0.5px;
    font-weight: 800;
  }

  .yellow { color: var(--brand); }

  .tagline {
    margin: 0;
    max-width: 34em;
    font-size: 18px;
    color: var(--text);
  }

  /* "Degoogle yourself!" on its own line, in the brand yellow — a real link to the manifesto
     page rather than a flourish. Underlined on hover so it reads as clickable without turning
     the tagline into a button when at rest. */
  .tagline .degoogle {
    color: var(--brand);
    font-weight: 700;
    text-decoration: none;
  }

  .tagline .degoogle:hover {
    text-decoration: underline;
    text-underline-offset: 4px;
  }

  /* One line between the device row and the security section: who the machine is for. Two weights —
     the ownership claim in white, the audience in brand yellow beneath it — so it reads as a single
     statement rather than a heading looking for a section. */
  .audience {
    margin: 4px 0 60px;
    text-align: center;
    font-size: 23px;
    font-weight: 800;
    letter-spacing: -0.2px;
    line-height: 1.35;
    color: var(--text);
  }

  .audience span {
    display: block;
    margin-top: 6px;
    font-size: 18px;
    font-weight: 600;
    color: var(--brand);
  }

  @media (max-width: 640px) {
    .audience { font-size: 19px; margin: 4px 0 44px; }
    .audience span { font-size: 15.5px; }
  }

  /* ── phones ───────────────────────────────────────────────────────────────── */

  section { padding: 8px 0 80px; }

  /* ── calls to action ──────────────────────────────────────────────────────── */

  .cta-row {
    text-align: center;
    padding: 0 0 48px;
    display: flex;
    justify-content: center;
    gap: 14px;
    /* One row at every width. Three one-word labels fit even on the narrowest phone once the pills
       shrink below, and a wrapped call-to-action row reads as two rows of something rather than
       one set of three. */
    flex-wrap: nowrap;
  }

  .cta {
    display: inline-block;
    padding: 14px 32px;
    border-radius: 999px;
    background: var(--brand);
    color: var(--ink);
    font-size: 17px;
    font-weight: 700;
    text-decoration: none;
    white-space: nowrap;
  }

  .cta:hover { filter: brightness(1.08); }

  .intro-flow { display: flex; flex-direction: column; }

  /* Wide enough for the two phones and the server to stand in one row: there the buttons read
     better underneath, as the thing you do after looking. Once that row wraps into a column the
     buttons have to come first again, or they land below a long scroll of stacked devices. */
  @media (min-width: 761px) {
    .intro-flow > section { order: 1; padding-bottom: 28px; }
    .intro-flow > .cta-row { order: 2; padding: 0 0 80px; }
  }

  /* Anchored sections need clearance, or the heading lands flush against the viewport edge. */
  #apps, #more, #phrases, #services, #security { scroll-margin-top: 28px; }

  /* ── section headings ─────────────────────────────────────────────────────── */

  h2 {
    margin: 0 0 28px;
    font-size: 30px;
    font-weight: 800;
    letter-spacing: -0.3px;
    text-align: center;
  }

  /* ── existing apps ────────────────────────────────────────────────────────── */

  /* Three across, and the same card surface as the bento below so the page reads as one system.
     Steps to two and then one — three cards side by side on a phone leaves no room for the copy,
     which is the whole reason these are cards rather than icons. */
  .apps {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 16px;
    margin-bottom: 72px;
  }

  .app-card {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 18px;
    padding: 24px;
    display: flex;
    flex-direction: column;
  }

  .app-head {
    display: flex;
    gap: 18px;
    align-items: flex-start;
    flex: 1 0 auto;
  }

  /* margin-top on a flex column child that follows a growing head pins this to the bottom, so the
     strips line up across a row even when one card's copy runs a line longer than its neighbour's. */
  .credit {
    margin: 18px 0 0;
    padding-top: 14px;
    border-top: 1px solid var(--line);
    font-size: 12px;
    color: var(--faint);
  }

  .credit .who { color: var(--muted); font-weight: 600; }

  /* The card WAS one big <a>. It is a div wrapping one now, because the credit strip below carries
     links of its own and an <a> inside an <a> is invalid — browsers close the outer one early and
     the layout comes apart. The inner link still fills everything above the strip, so the click
     target is unchanged. */
  .card-main { display: block; flex: 1 0 auto; text-decoration: none; color: inherit; }
  .app-card:hover { border-color: rgba(255, 255, 255, 0.34); background: var(--elevated); }

  /* A link to somebody else's source. Underlined in brand yellow rather than coloured outright,
     so a credit line stays a credit line and does not turn into a row of buttons. */
  a.src {
    color: inherit;
    text-decoration: underline;
    text-decoration-color: rgba(244, 196, 48, 0.55);
    text-underline-offset: 3px;
  }

  a.src:hover { color: var(--brand); text-decoration-color: var(--brand); }

  /* ── app pages ────────────────────────────────────────────────────────────── */

  .crumb { margin: 22px 0 0; font-size: 14px; }
  .crumb a { color: var(--muted); text-decoration: none; }
  .crumb a:hover { color: var(--text); }

  /* The hero on an app page is the same row as the home page's, but the icon is a fixed size:
     an app icon has nothing like the duck's presence and stretching it to the text height makes
     it look like a mistake. */
  .app-hero { justify-content: flex-start; align-items: center; }
  /* The mini-PC as hero art. It is a fixed-size CSS drawing rather than an image, so it needs the
     margin the icon gets from its own box and cannot inherit .duck's sizing. */
  .server-hero { gap: 40px; padding-bottom: 48px; }
  .server-hero .mini { flex: 0 0 auto; width: 180px; }

  .app-hero .duck { align-self: center; width: 132px; height: 132px; max-height: none; border-radius: 30px; }
  .app-hero h1 { font-size: 38px; }

  .credit-inline { margin: 10px 0 0; font-size: 13px; color: var(--faint); }
  .credit-inline .who { color: var(--muted); font-weight: 600; }

  @media (max-width: 640px) {
    .app-hero .duck { width: 104px; height: 104px; border-radius: 24px; }
    .app-hero h1 { font-size: 28px; }
  }

  .app-card img {
    width: 72px; height: 72px;
    border-radius: 17px;
    flex: 0 0 auto;
  }

  .app-card h3 {
    margin: 0 0 6px;
    font-size: 19px;
    font-weight: 700;
    color: var(--text);
  }

  .app-head p {
    margin: 0;
    font-size: 14px;
    line-height: 1.5;
    color: var(--muted);
  }

  @media (max-width: 980px) {
    .apps { grid-template-columns: repeat(2, 1fr); }
  }

  /* ── bento ────────────────────────────────────────────────────────────────── */

  .bento {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 16px;
    margin-top: 8px;
  }

  /* The wide cells top and bottom, four square-ish ones between — the shape asked for. */
  .bento .wide { grid-column: 1 / -1; }

  .cell {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 18px;
    padding: 26px 28px;
  }

  .cell h3 {
    margin: 0 0 8px;
    font-size: 20px;
    font-weight: 700;
    color: var(--text);
  }

  .cell p {
    margin: 0;
    font-size: 16px;
    color: var(--muted);
    line-height: 1.6;
  }

  .cell .lead { font-size: 18px; }

  /* The bento cell that carries the home page's duress treatment. Named for what it is rather than
     what it first held: on the Officer page it marks the duress panel, on the server page it marks
     the claim the whole page exists to make. Same tint, no danger reading implied. */
  .cell.accent {
    background: rgba(244, 196, 48, 0.08);
    border-left: 3px solid var(--brand);
    border-radius: 0 18px 18px 0;
  }

  a.cell.accent { display: block; text-decoration: none; color: inherit; }
  a.cell.accent:hover { background: rgba(244, 196, 48, 0.14); border-color: var(--brand); }

  /* The affordance. Without it a tinted panel is just a tinted panel — nothing about a bento cell
     says "this one goes somewhere", and a link nobody knows is a link may as well be static. */
  .more {
    margin-top: 16px !important;
    color: var(--brand) !important;
    font-weight: 600;
    font-size: 15px !important;
  }

  /* ── bridges ──────────────────────────────────────────────────── */

  /* Small and dense on purpose. The argument is the LIST — eight apps, eight named upstreams — so
     the eye needs to take the whole column in at once. Long cards would turn a single point into
     eight paragraphs and lose it. */
  .bridges {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 14px;
    margin-bottom: 16px;
  }

  .bridge {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 16px;
    padding: 20px 22px;
  }

  .bridge h3 { margin: 0 0 4px; font-size: 18px; font-weight: 700; }
  .bridge p { margin: 0; font-size: 14.5px; line-height: 1.6; color: var(--muted); }

  .upstream {
    margin-bottom: 10px !important;
    font-size: 11px !important;
    font-weight: 700;
    letter-spacing: 1.1px;
    text-transform: uppercase;
    color: var(--faint) !important;
  }

  .upstream span { color: var(--brand); }

  /* The two with nothing upstream to bridge to. Marked in teal rather than left to read as an
     oversight — the point of the section survives only if the exceptions are visible. */
  .bridge.ours .upstream span { color: #5eb7c9; }

  .bridges-close { margin-top: 16px; }
  .bridges-close p { margin: 0; }

  @media (max-width: 760px) {
    .bridges { grid-template-columns: 1fr; }
  }

  /* ── demos ────────────────────────────────────────────────────── */

  .demos-hero { padding-bottom: 20px; }
  .demo { padding-top: 0; }
  .demo h2 { margin-bottom: 14px; }

  .demo-meta {
    margin: 0 auto 22px;
    max-width: 760px;
    text-align: center;
    font-size: 13px;
    color: var(--faint);
  }

  .demo-meta span { color: var(--brand); font-weight: 700; }

  /* Portrait phone footage: cap the WIDTH or a 760x1576 recording stands taller than the window and
     the reader scrolls a video instead of watching one. The height attribute on the element keeps
     the space reserved before the poster arrives, so nothing below it jumps. */
  .demo-video {
    display: block;
    margin: 0 auto;
    width: 100%;
    max-width: 380px;
    height: auto;
    border-radius: 14px;
    background: var(--bg);
  }

  .demo-fallback { color: var(--muted); text-align: center; }

  .demo-more { margin: 20px 0 0; text-align: center; font-size: 15px; }
  .demo-more a { color: var(--brand); font-weight: 600; text-decoration: none; }
  .demo-more a:hover { text-decoration: underline; text-underline-offset: 4px; }

  /* ── the phrase ladder ────────────────────────────────────────── */

  .ladder { margin-top: 8px; }

  .section-lead {
    margin: -12px auto 30px;
    max-width: 760px;
    text-align: center;
    font-size: 17px;
    color: var(--muted);
    line-height: 1.6;
  }

  /* One per row, full width, in order. The reading order IS the escalation — a grid would let the
     eye start anywhere, and starting at the nuclear option is the wrong first impression of a
     feature whose first tier costs you a login. */
  .phrases { display: flex; flex-direction: column; gap: 14px; }

  .phrase {
    background: var(--surface);
    border: 1px solid var(--line);
    border-left: 3px solid var(--brand);
    border-radius: 0 18px 18px 0;
    padding: 24px 28px;
  }

  /* The heaviest live tier. Nothing below it in the DOM is louder, so the colour does not need to
     escalate across all four — it only has to break once, at the rung where it matters. */
  .phrase:nth-of-type(3) { border-left-color: var(--alarm); }
  .phrase:nth-of-type(3) .scope { color: var(--alarm); border-color: rgba(229, 103, 92, 0.4); }

  /* Not built. Dimmed and dashed so it cannot be mistaken for the three above it at a glance —
     the label alone would be doing that work in 11px type. */
  .planned {
    background: transparent;
    border-style: dashed;
    border-left-style: dashed;
    border-left-color: var(--line);
    opacity: 0.72;
  }

  .phrase-head {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 10px;
  }

  .phrase h3 { margin: 0; font-size: 21px; font-weight: 700; }

  .scope {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 1.1px;
    text-transform: uppercase;
    color: var(--brand);
    border: 1px solid rgba(244, 196, 48, 0.4);
    border-radius: 999px;
    padding: 3px 11px;
  }

  .soon {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 1.1px;
    text-transform: uppercase;
    color: var(--faint);
  }

  .phrase p { margin: 0; font-size: 15.5px; line-height: 1.65; color: var(--muted); }

  /* What it costs to undo. This is the field that actually distinguishes the tiers, so it gets a
     fixed position at the foot of every row — readable as a column of its own down the page. */
  .recover {
    margin-top: 14px !important;
    padding-top: 13px;
    border-top: 1px solid var(--line);
    font-size: 14px !important;
    color: var(--faint) !important;
  }

  .recover span {
    display: inline;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 1.1px;
    text-transform: uppercase;
    color: var(--muted);
    margin-right: 10px;
  }

  @media (max-width: 640px) {
    .phrase { padding: 20px 22px; }
    .phrase h3 { font-size: 19px; }
  }

  /* ── the three pillars ────────────────────────────────────────── */

  /* Full-width rows rather than a third column each. These carry three paragraphs where a grid card
     carries one sentence, and long copy in a narrow column is what makes a page look unread. The
     width is the emphasis — sitting apart from the grid is what marks them as the important three. */
  .pillars {
    display: flex;
    flex-direction: column;
    gap: 16px;
    margin-bottom: 44px;
  }

  .pillar {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 18px;
    padding: 30px 32px;
  }

  .pillar-main {
    display: flex;
    gap: 30px;
    align-items: flex-start;
    text-decoration: none;
    color: inherit;
  }

  .pillar:hover { border-color: rgba(255, 255, 255, 0.34); background: var(--elevated); }

  /* Indent past the icon so the credit lines up with the copy it belongs to, rather than starting
     under the artwork. 104px icon + 30px gap. */
  .pillar > .credit { margin-left: 134px; }

  .pillar-main > img {
    width: 104px; height: 104px;
    border-radius: 24px;
    flex: 0 0 auto;
  }

  .pillar-body { flex: 1; min-width: 0; }

  .pillar h3 {
    margin: 0;
    font-size: 24px;
    font-weight: 800;
    letter-spacing: -0.2px;
  }

  /* The one line someone reads if they read nothing else in the card. */
  .claim {
    margin: 4px 0 14px;
    font-size: 18px;
    font-weight: 600;
    color: var(--brand);
    line-height: 1.4;
  }

  .pillar p { margin: 0; font-size: 15px; line-height: 1.65; color: var(--muted); }

  /* The philosophy, set apart from the description of what the thing does — a rule in the brand
     yellow rather than another heading, which would break the card into two cards. */
  .why {
    margin-top: 16px !important;
    padding-left: 18px;
    border-left: 3px solid var(--brand);
    color: var(--faint) !important;
  }

  /* The duress note. A filled panel rather than another bare rule: it is the strongest thing on the
     page and reading as a second footnote to the philosophy above would undersell it. Officer is the
     only card that has one, and the asymmetry is the point. */
  .duress {
    margin-top: 14px !important;
    padding: 15px 18px;
    border-left: 3px solid var(--brand);
    border-radius: 0 12px 12px 0;
    background: rgba(244, 196, 48, 0.08);
    color: var(--muted) !important;
  }

  .why span, .duress span {
    display: block;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 1.2px;
    text-transform: uppercase;
    color: var(--brand);
    margin-bottom: 4px;
  }

  .pillar .credit { border-top: none; padding-top: 0; margin-top: 16px; }

  /* Marks the grid below as the remainder, so twelve cards do not read as a demotion of the three
     above them — they are the rest of the suite, not the leftovers. */
  .subhead {
    margin: 0 0 20px;
    font-size: 14px;
    font-weight: 700;
    letter-spacing: 1.6px;
    text-transform: uppercase;
    color: var(--faint);
    text-align: center;
  }

  @media (max-width: 760px) {
    /* flex-direction belongs on .pillar-main, NOT .pillar. The card became a plain block when the
       credit strip moved out of its link, and this rule sat on the block for several commits doing
       nothing at all — which is why the copy stayed in a narrow column beside the icon. */
    .pillar { padding: 26px 24px; }
    .pillar-main { flex-direction: column; gap: 20px; }
    .pillar > .credit { margin-left: 0; }
    .pillar-main > img { width: 84px; height: 84px; border-radius: 19px; }
    .pillar h3 { font-size: 21px; }
    .claim { font-size: 16px; }
  }

  /* The one card that introduces the app grid rather than sitting in a grid of its own. */
  .intro { margin-bottom: 28px; }

  @media (max-width: 640px) {
    .apps { grid-template-columns: 1fr; }

    /* Icon over text instead of beside it. A 72px icon and an 18px gap take 90px off the line
       length, which on a 375px phone leaves the blurb about 173px wide — four or five words a line,
       broken across six. Stacking gives the copy the whole card. */
    .app-head { flex-direction: column; gap: 14px; }
    .bento { grid-template-columns: 1fr; }
    .bento .wide { grid-column: auto; }
  }


  .phones {
    display: flex;
    justify-content: center;
    align-items: flex-start;
    gap: 40px;
    flex-wrap: wrap;
  }

  .phone {
    width: 232px;
    background: #0e141a;
    border: 2px solid #39424e;
    padding: 10px;
    position: relative;
  }

  /* iPhone: tall rounded corners and a pill island. Android: squarer, with a hole-punch. The
     two silhouettes are what say "one of each" without a word of label. */
  .phone.ios { border-radius: 40px; }
  .phone.android { border-radius: 26px; }

  .screen {
    border-radius: 30px;
    background: linear-gradient(160deg, #1a2632 0%, #141b23 55%, #10211c 100%);
    padding: 30px 14px 16px;
    min-height: 400px;
    position: relative;
    overflow: hidden;
    display: flex;
    flex-direction: column;
  }

  .phone.android .screen { border-radius: 18px; }

  .island {
    position: absolute;
    top: 8px; left: 50%;
    transform: translateX(-50%);
    width: 62px; height: 15px;
    background: #05070a;
    border-radius: 10px;
  }

  .punch {
    position: absolute;
    top: 10px; left: 50%;
    transform: translateX(-50%);
    width: 10px; height: 10px;
    background: #05070a;
    border-radius: 50%;
  }

  /* Three across, not four. 180px of usable screen over three columns gives a 55px cell, so the
     icons can be 50px instead of 42 — at the size these mockups render, four columns made the
     icons too small to recognise, which defeats the point of showing them at all. */
  .grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 14px 8px;
  }

  .app { text-align: center; }

  .app img {
    width: 50px; height: 50px;
    display: block;
    margin: 0 auto 4px;
  }

  /* The one real difference between the two mockups: iOS rounds icons into a squircle, most
     Android launchers mask them to a circle. Same source art, masked per platform — which is
     exactly what the two operating systems do to it. */
  .ios .app img { border-radius: 10px; }
  .android .app img { border-radius: 50%; }

  .app span {
    display: block;
    font-size: 9px;
    color: var(--faint);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .dock {
    /* Pinned to the bottom of the screen rather than trailing the grid — a dock floating in the
       middle of the display is the detail that stops these reading as phones. */
    margin-top: auto;
    padding: 8px 5px;
    border-radius: 18px;
    background: rgba(255, 255, 255, 0.06);
    /* Three here too. A dock whose icons are smaller than the grid's is the one thing no real phone
       does, and it is immediately obvious even at this size. */
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 8px;
  }

  .android .dock { border-radius: 12px; }
  .dock img { width: 50px; height: 50px; display: block; margin: 0 auto; }
  .ios .dock img { border-radius: 12px; }
  .android .dock img { border-radius: 50%; }

  /* ── the server ───────────────────────────────────────────────────────────── */

  /* A mini-PC rather than a rack: the whole point is that this is one small box on a shelf, not
     infrastructure. Drawn in CSS for the same reason everything else here is — no extra request. */
  /* A link, not a caption: the mini-PC sits between the phones as the thing they both talk to, so
     it is the most natural place on the page to ask about the server. */
  .server { align-self: center; width: 210px; text-decoration: none; color: inherit; display: block; }
  /* Underlined, and scoped to .server so the iPhone and Android captions beside it — which are not
     links — do not pick it up. The offset keeps the rule clear of the descender in "Your". */
  .server .platform {
    text-decoration: underline;
    text-decoration-thickness: 2px;
    text-underline-offset: 6px;
    text-decoration-color: rgba(255, 255, 255, 0.4);
  }

  .server:hover .platform { color: var(--brand); text-decoration-color: var(--brand); }
  /* Hover emphasis: the server box grows from its centre (scale()'s default origin) and its
     shadow deepens, so it reads as rising toward you. Pure CSS — no JS, Tor-safe. */
  .server:hover .mini {
    transform: scale(1.12);
    box-shadow: 0 22px 44px rgba(0, 0, 0, 0.62);
  }
  /* Respect users who ask for less motion: keep the emphasis but drop the animation. */
  @media (prefers-reduced-motion: reduce) {
    .mini { transition: none; }
  }

  .mini {
    height: 76px;
    border-radius: 12px;
    background: linear-gradient(180deg, #cdd3d9 0%, #a8b0b8 46%, #7d868f 100%);
    border: 1px solid #dfe4e8;
    position: relative;
    box-shadow: 0 14px 28px rgba(0, 0, 0, 0.55);
    overflow: hidden;
    transition: transform 180ms ease, box-shadow 180ms ease;
  }

  /* Vent slots, as a repeating gradient rather than a row of elements. */
  .mini::before {
    content: "";
    position: absolute;
    left: 14px; right: 14px; bottom: 12px;
    height: 16px;
    background: repeating-linear-gradient(
      90deg,
      rgba(0, 0, 0, 0.32) 0 2px,
      transparent 2px 6px
    );
    border-radius: 2px;
  }

  /* Green, because it is a status light rather than branding: on a machine, green means running.
     It was the Officer yellow, which read as decoration on a box whose whole job is to be up. */
  .mini::after {
    content: "";
    position: absolute;
    top: 14px; left: 16px;
    width: 7px; height: 7px;
    border-radius: 50%;
    background: #22c55e;
    box-shadow: 0 0 8px 1px rgba(34, 197, 94, 0.7);
  }

  .mini-top {
    position: absolute;
    top: 0; left: 0; right: 0;
    height: 1px;
    background: rgba(255, 255, 255, 0.75);
  }

  .platform {
    text-align: center;
    margin-top: 16px;
    font-size: 24px;
    font-weight: 600;
    letter-spacing: 0.2px;
    color: var(--muted);
  }

  /* ── footer ───────────────────────────────────────────────────────────────── */

  footer {
    margin-top: auto;
    border-top: 1px solid var(--line);
    padding: 28px 0 40px;
    text-align: center;
    color: var(--muted);
    font-size: 26px;
  }

  footer a { color: var(--muted); }

  @media (max-width: 640px) {
    header { flex-direction: column; align-items: center; text-align: center; padding: 24px 0 36px; }
    .duck { align-self: center; max-height: 120px; }
    .hero-text { align-items: center; }
    h1 { font-size: 30px; }
    .tagline { max-width: none; }
    /* Mobile layout: the server floats to the top (right under the CTA buttons), then the two
       phones sit side by side below it. flex-wrap + order:-1 does the reorder; the phones shrink
       to share one row without breaking their 3-column app grids. */
    /* Tighten the space between the CTAs and the server area. */
    .cta-row { padding-bottom: 16px; }
    section { padding-top: 0; }
    .phones { gap: 16px 14px; align-items: flex-start; }
    /* Phone captions are ordered ABOVE their phones (order:-1 in a column flex). The server
       caption is instead overlaid ON the box — see below. */
    .server { order: -1; flex: 0 0 100%; width: auto; display: flex; flex-direction: column; align-items: center; position: relative; }
    /* "Your server" overlaid on the box (centred, higher z-index). Dark with a light text-shadow
       so it reads on the grey device; pointer-events off so it never blocks the underlying link. */
    .server .platform {
      position: absolute; z-index: 2;
      top: 36%; left: 50%; transform: translate(-50%, -50%);
      margin: 0; color: #0e141a; text-decoration: none;
      text-shadow: 0 1px 3px rgba(255, 255, 255, 0.6);
      pointer-events: none; white-space: nowrap;
    }
    /* width:100% because a flex item under align-items:center won't stretch — without it the
       empty mini-PC box collapses to a sliver. max-width keeps it from spanning the whole row. */
    .server .mini { width: 100%; max-width: 280px; }
    .phones > div { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 8px; }
    .phones > div .platform { order: -1; margin: 0; font-size: 12.5px; }
    .phone { width: 100%; max-width: 170px; padding: 6px; }
    .screen { min-height: 280px; padding: 18px 6px 10px; }
    .grid { gap: 9px 4px; }
    .app img, .dock img { width: 32px; height: 32px; }
    .app span { font-size: 8px; }
    .dock { gap: 4px; padding: 6px 4px; }
    .platform { font-size: 19px; }
    footer { font-size: 18px; padding: 22px 0 28px; }
    /* Four pills in one no-wrap row across all of mobile. The longest label ("Services") is what
       sets the budget, so the pills carry one compact size for the whole ≤640 range rather than a
       larger size that only fits the short labels — this holds all four on a line down to ~340px. */
    .cta-row { gap: 6px; }
    .cta { padding: 12px 12px; font-size: 13px; }
  }

  /* The narrowest phones still in use: just pull the page gutter in so the same compact pills keep
     their breathing room against the screen edge. (The pill size itself is set once above and needs
     no further shrink here.) */
  @media (max-width: 380px) {
    .wrap { padding: 0 18px; }
  }

  /* ── the reading page (degoogle-yourself) ──────────────────────────────────── */

  /* Anchored manifestos need the same clearance the home page gives its sections. */
  .manifesto { scroll-margin-top: 28px; }

  /* Three texts, three anchors, one line. The links reuse .src's yellow underline. */
  .toc {
    margin: -14px 0 0;
    text-align: center;
    font-size: 15px;
    color: var(--faint);
  }

  /* The byline under each manifesto heading — the credit is part of the text, not a footnote. */
  .manifesto-meta {
    margin: -16px 0 0;
    text-align: center;
    font-size: 14px;
    color: var(--faint);
  }

  /* One editorial note is allowed outside .prose: the FSF's own framing of the GNU text — who
     wrote it, when, and why the footnotes exist. Everything inside .prose is verbatim. */
  .manifesto-note {
    margin: 22px auto 0;
    max-width: 44em;
    text-align: center;
    font-size: 14px;
    line-height: 1.6;
    color: var(--faint);
  }

  /* The one long-form page on the site. Everywhere else the grid is the layout; a 3,000-word
     manifesto in a card column would be unreadable, so these get a single centred measure. */
  .prose {
    max-width: 46em;
    margin: 40px auto 0;
    font-size: 17px;
    line-height: 1.75;
    color: var(--muted);
  }

  .prose p { margin: 0 0 1.4em; }

  /* The GNU text has sections of its own; h4 keeps them visibly below the page's h2/h3. */
  .prose h4 {
    margin: 2em 0 0.7em;
    font-size: 20px;
    font-weight: 700;
    color: var(--text);
  }

  /* Quoted objections, and the one nested enumeration (the Software Tax). */
  .prose blockquote {
    margin: 0 0 1.4em;
    padding-left: 18px;
    border-left: 3px solid var(--line);
    color: var(--faint);
  }

  .prose blockquote p { margin-bottom: 0.7em; }
  .prose blockquote > :last-child { margin-bottom: 0; }

  .prose ol, .prose ul { margin: 0 0 1.4em; padding-left: 1.5em; }
  .prose li { margin-bottom: 0.7em; }
  .prose li:last-child { margin-bottom: 0; }

  .signoff { color: var(--faint); }

  /* The notices the licences require to travel with the texts — small, never omitted. */
  .fineprint {
    max-width: 46em;
    margin: 28px auto 0;
    padding-top: 16px;
    border-top: 1px solid var(--line);
    font-size: 12.5px;
    line-height: 1.6;
    color: var(--faint);
  }
