/* css/island.css — الجزيرة: the island.

   One object, many shapes. The site used to carry three separate controls —
   a header that drew itself in on scroll, a lens bar that lived inside the
   lesson's iframe, and an account menu that hung off the corner. They were
   three because nothing had decided what the site's one instrument was.

   This is that instrument. It is a brass-rimmed plate on the chart table: at
   rest a pill with the wordmark on it, on a subject sheet the sheet's name,
   and inside a lesson the full sounding gear — where you are, how far in,
   what you kept. It never appears and never leaves. It changes shape.

   THE GEOMETRY CONTRACT
   The rail (.main-header) owns a height that never changes; --header-h is a
   fact the hero pulls itself up by, and a bar that grew while stuck would
   nudge the page on the first scroll. So the island's *bar* is pinned to
   --header-bar-h forever, and everything that grows — every drawer — is
   absolutely positioned beneath it and cannot be felt by the flow.

   THE LAW OF THE TWO METALS
   Water carries where you are: --depth-2 fills the island's own rule as you
   read, and the readout is set in the soundings face. Brass carries what you
   chose: a kept section, the open drawer's tab, the resume pill. Nothing else
   on this object is coloured in. It is the same law css/python-reader-features
   draws the sounding line under, which is what keeps the shell and the lesson
   reading as one machine.
   ========================================================================== */

:root {
  /* The bar's inner height. Pinned — see the geometry contract. */
  --island-h: var(--header-bar-h);
  /* How far a drawer may fall before it takes a scrollbar of its own. Held to
     the viewport so a long outline on a short laptop never runs off the
     bottom of the screen with no way back to it. */
  --island-drawer-max: min(62vh, 30rem);
  --island-gap: 0.45rem;
  /* The plate's own ink. A rule this thick is the world's, not a border I
     picked: the cards, the footer plate and the lesson's bar all carry it. */
  --island-rule-w: 3px;
}

/* HIDDEN MEANS HIDDEN.

   `hidden` is a UA rule — `[hidden] { display: none }` — and the UA sheet is
   the weakest sheet there is. Every control on this plate is laid out with an
   explicit `display: flex` or `inline-flex`, and each one of those beats it.
   The result was a print button on the home page, a bookmark ribbon with no
   section to keep, and a lesson readout on a screen with no lesson: the
   controller was setting `hidden` correctly and the stylesheet was ignoring
   it, on every single control at once.

   So the island says it once, for everything inside it, and no rule below may
   set `display` on an element without this standing over it. The island's
   whole design is one object showing a different set of controls per screen,
   which makes this line load-bearing rather than defensive. */
.island [hidden],
.island-drawer [hidden] {
  display: none !important;
}

/* --------------------------------------------------------------- the plate */

.island {
  /* Absolutely positioned inside the rail, and that is what buys the drawer
     the right to be a real child instead of a box hung underneath.

     The rail's height is a contract — --header-h is a fact the hero pulls
     itself up by, and a bar that grew while stuck would nudge the page on the
     first scroll. The old drawer honoured that by being `position: absolute`
     itself: its own border, its own corner, its own shadow, sitting under a
     bar that still had a bottom edge of its own. Two boxes touching, and it
     read as exactly that — a rule across the seam, corners that did not
     agree, and six pixels of overhang either side.

     Taking the *plate* out of the flow instead solves it at the root. The
     rail keeps its height whatever the island does, so the island can be one
     box: one border, one corner, one shadow, with the bar and the drawer as
     two children of it. The seam is gone because there is no seam. */
  position: absolute;
  inset-inline: 0;
  margin-inline: auto;
  width: fit-content;
  max-width: min(100% - 1.5rem, 62rem);

  display: flex;
  flex-direction: column;
  isolation: isolate;

  background-color: var(--bg-secondary);

  /* THE DOUBLE RULE.

     A chart's title block is not drawn with a border, it is drawn with two: a
     heavy outer rule, a hair of paper, and a fine inner rule inside that. It
     is the single most recognisable piece of chart draughting there is, and
     it is what tells you at a glance that the panel is part of the survey
     rather than something printed over it.

     Outer is the border; the gap is 3px of the plate's own ground; inner is a
     1px inset ring. Three declarations, no extra element, and it survives
     every corner radius the plate morphs through. */
  border: var(--island-rule-w) solid var(--border-color);
  box-shadow:
    inset 0 0 0 3px var(--bg-secondary),
    inset 0 0 0 4px color-mix(in srgb, var(--border-color) 55%, transparent),
    var(--shadow-md);

  /* Rectangular. A title block is a rectangle — the pill was the one thing on
     this object that came from a phone's notch rather than from a chart, and
     it was doing the most to make the plate read as somebody else's. 3px is
     the corner the site's own plates are cut with. */
  border-radius: var(--radius-xs);
  overflow: hidden;

  transition:
    min-width var(--dur-morph) var(--ease-morph),
    box-shadow var(--dur-morph) var(--ease-morph),
    background-color var(--transition-normal);
}

/* Open: the block gains the panel below the crease, and lifts because there
   is more of it to lift. The corner does not change — a title block does not
   round off because you unfolded the sheet. */
.island[data-drawer]:not([data-drawer=""]) {
  min-width: min(34rem, calc(100% - 1.5rem));
  box-shadow:
    inset 0 0 0 3px var(--bg-secondary),
    inset 0 0 0 4px color-mix(in srgb, var(--border-color) 55%, transparent),
    var(--shadow-lg);
}

/* Scrolled. The plate does not change size — it changes material: the ground
   goes to glass so the chart keeps running underneath it, which is the one
   thing that says the page is moving behind the instrument. */
.main-header.is-scrolled .island {
  background-color: var(--glass-tint);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .main-header.is-scrolled .island { background-color: var(--bg-secondary); }
}

/* ----------------------------------------------------------------- the bar */

.island-bar {
  display: flex;
  align-items: center;
  gap: var(--island-gap);
  /* Pinned. This is the geometry contract, written as a number. The sounding
     scale hangs inside this box rather than under it, so it costs nothing and
     the block is the same height on every screen. */
  position: relative;
  flex: none;
  height: calc(var(--island-h) - (2 * var(--island-rule-w)));
  padding-inline: 0.5rem;
  position: relative;
  z-index: 2;
}

/* The three slots, and what each one is for. Lead is where you came from,
   body is what you are looking at, tools is what you can do to it. A control
   that moves between slots when the context changes is a control that has not
   decided which of those three it is. */
.island-slot {
  display: flex;
  align-items: center;
  min-width: 0;
}

.island-slot-lead  { gap: 0.35rem; flex: none; }
.island-slot-body  { gap: 0.6rem;  flex: 1 1 auto; min-width: 0; }
.island-slot-tools { gap: 0.3rem;  flex: none; }

/* ------------------------------------------------------------ the wordmark */

.island-brand {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding-inline: 0.35rem 0.2rem;
  text-decoration: none;
  color: inherit;
  min-width: 0;
}

.island-brand-avatar {
  width: 34px;
  height: 34px;
  flex: none;
  border-radius: var(--radius-full);
  border: 2.5px solid var(--border-color);
  object-fit: cover;
  /* The tilt the avatar has carried since the first header. Kept because it
     is the one thing on this plate that is not square to the world. */
  transform: rotate(-5deg);
  background: var(--bg-tertiary);
}

.island-brand-text {
  display: flex;
  flex-direction: column;
  line-height: 1;
  min-width: 0;
}

.island-brand-title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.02rem;
  letter-spacing: -0.02em;
  color: var(--text-primary);
  white-space: nowrap;
}

.island-brand-title span { color: var(--primary); }

/* The line under the wordmark is not a tagline — it is where you are. It
   carries the level and the department once a student has chosen one, which
   is the only thing about them the index knows. */
.island-brand-place {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-block-start: 0.25rem;
  /* Collapses rather than disappears: the wordmark must not jump up by a line
     when the place is not known yet. */
  max-height: 1em;
  transition: max-height var(--dur-state) var(--ease-out),
              opacity var(--dur-state) var(--ease-out),
              margin-block-start var(--dur-state) var(--ease-out);
}

.island-brand-place:empty,
.island.is-condensed .island-brand-place {
  max-height: 0;
  margin-block-start: 0;
  opacity: 0;
}

/* --------------------------------------------------------------- the place */

/* What the island says it is showing, on a sheet or in a lesson. dir="auto"
   in the markup because most of these names are Latin inside an RTL plate:
   without it the ellipsis eats the beginning of "Introduction to Python"
   instead of its tail. */
.island-place {
  /* Two registers on a grid, not a row of text. A title block stacks its
     label over its value and rules the columns to the same baseline — which
     is also the only way to fit "which sheet" and "which part of it" into a
     bar this tall without either of them shrinking to nothing. */
  display: grid;
  grid-template-areas:
    "label"
    "name"
    "section";
  align-content: center;
  gap: 0.05rem;
  min-width: 0;
  margin: 0;
}

/* The label. Tracked hard and set small in the soundings face, which is how
   every field on a chart is lettered — the tracking is the tell, not the
   size. It names the register under it and nothing else. */
.island-place-label {
  grid-area: label;
  font-family: var(--font-mono);
  font-size: 0.56rem;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--text-muted);
  white-space: nowrap;
  line-height: 1.1;
}

.island-place-label:empty { display: none; }

.island-place-name {
  grid-area: name;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.02rem;
  letter-spacing: -0.01em;
  line-height: 1.15;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The part you are standing in, under the sheet's name. Live, from the
   reader — the one thing on screen still saying what you are reading once the
   heading has scrolled a page and a half above you. */
.island-place-section {
  grid-area: section;
  font-family: var(--font-body);
  font-size: 0.72rem;
  font-weight: 500;
  line-height: 1.25;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

.island-place-section:empty { display: none; }

/* --------------------------------------------------------------- the buttons */

/* One button, in the plate's own vocabulary: an ink rule, a hard shadow, and
   it presses into the page rather than lifting off it. Every control on this
   object is this button or a variant of it — a stock control on a committed
   plate is the seam that gives an interface away. */
.island-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  flex: none;
  height: 34px;
  min-width: 34px;
  padding-inline: 0.5rem;
  font-family: var(--font-display);
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--text-primary);
  background: var(--bg-secondary);
  border: 2px solid var(--border-color);
  /* Cut like the plate they sit on. Circular buttons were the other thing on
     this object borrowed from a phone rather than from a chart — a ruled
     rectangle beside a row of discs reads as two design systems sharing a
     bar. 3px, the same corner as everything else the site draws. */
  border-radius: var(--radius-xs);
  box-shadow: 2px 2px 0 var(--shadow-color);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform var(--transition-fast),
              box-shadow var(--transition-fast),
              background-color var(--transition-fast),
              color var(--transition-fast);
}

.island-button .lesson-icon { width: 18px; height: 18px; }

.island-button:hover {
  transform: translate(1px, 1px);
  box-shadow: 1px 1px 0 var(--shadow-color);
}

.island-button:active {
  transform: translate(2px, 2px);
  box-shadow: 0 0 0 var(--shadow-color);
}

.island-button:focus-visible {
  outline: 3px solid var(--accent-yellow);
  outline-offset: 2px;
}

/* Pressed and held: this button's drawer is the one that is open. Brass,
   because an open drawer is a thing you chose. */
.island-button[aria-expanded="true"],
.island-button.is-on {
  background: var(--brass-fill);
  border-color: var(--border-color);
  color: var(--brass-on);
  transform: translate(2px, 2px);
  box-shadow: 0 0 0 var(--shadow-color);
}

.island-button[disabled] {
  opacity: 0.42;
  cursor: not-allowed;
  transform: none;
  box-shadow: 2px 2px 0 var(--shadow-color);
}

/* The label inside a button, which the condensed island drops. The icon
   always stays: an island that loses its icons has lost its controls. */
.island-button-label { white-space: nowrap; }

@media (max-width: 900px) {
  .island-button-label { display: none; }
  .island-button { padding-inline: 0.35rem; }
}

/* ------------------------------------------------------------- the readout */

/* Where you are among the parts, in the soundings face. Latin figures and
   padded — 07/48 beside 24/48 keeps the pair the same width, so the readout
   does not shuffle sideways every time you cross a heading. It is a button:
   pressing it opens the lesson's parts. */
.island-readout {
  /* A field of the block: its label above, its figures below, ruled off from
     the row on the reading side. Padded to a constant width — 07/48 beside
     24/48 is the same span, so the block does not shuffle sideways every time
     you cross a heading. */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.05rem;
  flex: none;
  padding: 0.1rem 0.55rem 0.1rem 0;
  border: 0;
  border-inline-end: 2px solid var(--border-light);
  background: transparent;
  cursor: pointer;
  text-align: start;
  transition: color var(--transition-fast);
}

.island-readout::before {
  content: "السبر";
  font-family: var(--font-mono);
  font-size: 0.56rem;
  font-weight: 600;
  letter-spacing: 0.22em;
  color: var(--text-muted);
  line-height: 1.1;
}

.island-readout-figures {
  font-family: var(--font-mono);
  font-size: 0.95rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
  line-height: 1.15;
  color: var(--depth-3);
  direction: ltr;
}

.island-readout:hover .island-readout-figures { color: var(--primary); }

.island-readout:focus-visible {
  outline: 3px solid var(--accent-yellow);
  outline-offset: 2px;
}

/* --------------------------------------------------------- the sounding scale */

/* A chart prints its depth scale along the margin: a ruled bar, ticks at
   even intervals, and figures under them, so a depth read off the water can
   be found on the paper. This is that bar, and the lesson is what it
   measures.

   It replaced a 3px band that filled with colour from one end. The band said
   one number badly — a percentage, which on a document that is long by design
   is the least useful thing you can be told. The scale says three: how far
   the line has run out, how many parts there are, and how far apart they sit,
   because the ticks are the parts themselves. That is the same instrument the
   reader draws down the side of the page, brought onto the plate at the
   scale a bar can hold.

   Inside the bar and pinned to its foot, so it costs no height: the block's
   geometry is a contract (see the top of this file) and a row that appears
   only inside a lesson would break it. */
.island-scale {
  position: absolute;
  inset-inline: 0;
  inset-block-end: 0;
  height: 7px;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-state) var(--ease-out);
}

.island[data-context="reader"] .island-scale { opacity: 1; }

/* The ticks. Drawn as a gradient rather than as elements because there are as
   many of them as the lesson has parts — 48 on NumPy — and forty-eight spans
   in the DOM to draw forty-eight hairlines is forty-eight spans too many.
   --island-ticks is the spacing, written by js/island.js from the count. */
.island-scale-ticks {
  position: absolute;
  inset: 0;
  border-block-start: 2px solid var(--border-light);
  background-image: repeating-linear-gradient(
    to right,
    color-mix(in srgb, var(--depth-2) 55%, transparent) 0 1px,
    transparent 1px var(--island-tick, 8px)
  );
  /* The ticks fade out toward the far end, the way a scale bar's do where the
     survey runs off the sheet. */
  mask-image: linear-gradient(to left, transparent, #000 12%);
  -webkit-mask-image: linear-gradient(to left, transparent, #000 12%);
}

/* The line already paid out. Water, from the reading edge inward. */
.island-scale-ticks::before {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  width: var(--island-progress, 0%);
  background: color-mix(in srgb, var(--depth-2) 22%, transparent);
  border-block-start: 2px solid var(--depth-2);
  transition: width var(--dur-move) linear;
}

/* The weight on the end of the line. Brass, because on this object brass is
   what the student's own position is drawn in — the same law the kept
   sections and the open tab are painted under. */
.island-scale-weight {
  position: absolute;
  inset-block-start: -1px;
  inset-inline-start: var(--island-progress, 0%);
  width: 9px;
  height: 9px;
  margin-inline-start: -4px;
  background: var(--brass-fill);
  border: 1.5px solid var(--border-color);
  /* A plumb bob, not a dot: square at the top, pointed at the bottom. */
  clip-path: polygon(0 0, 100% 0, 100% 55%, 50% 100%, 0 55%);
  transition: inset-inline-start var(--dur-move) linear;
}

@media (prefers-reduced-motion: reduce) {
  .island-scale-ticks::before,
  .island-scale-weight { transition: none; }
}

/* -------------------------------------------------------------- the drawer */

/* What the island holds when it is holding something. Absolutely positioned,
   so the rail's height is untouched and nothing under the header can feel it
   open — the geometry contract, enforced.

   It is not a menu that appears near the island. It is the island's own body
   continuing downward: same ground, same rule, same corner, and the seam
   between them is a single hairline rather than two objects meeting. */
.island-drawer {
  /* In flow, inside the plate. It has no border, no corner and no shadow of
     its own — it is not a box, it is the lower half of one, and the plate it
     is inside already draws all three. Closed is height zero rather than
     display:none: a box with no layout cannot animate to one. */
  position: relative;
  max-height: 0;
  overflow: hidden;
  visibility: hidden;
  transition: max-height var(--dur-move) var(--ease-out),
              visibility 0s linear var(--dur-move);
}

/* THE CREASE.

   A chart is not a menu that slides; it is a sheet that unfolds. So the seam
   between the block and the panel is drawn as the fold it would be: a rule
   with the paper falling away from it in shadow on the panel's side, the way
   a sheet opened flat still shows where it was folded.

   This is the one place on the object where the world is being drawn rather
   than the mechanism, and it is the whole of it — the rest of the block is
   ruled and typeset and does not decorate. */
.island-drawer::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  inset-block-start: 0;
  height: 10px;
  z-index: 3;
  pointer-events: none;
  background:
    linear-gradient(to bottom,
      color-mix(in srgb, var(--border-color) 55%, transparent) 0 1px,
      color-mix(in srgb, var(--shadow-color) 12%, transparent) 1px,
      transparent 100%);
}

.island[data-drawer]:not([data-drawer=""]) .island-drawer {
  max-height: var(--island-drawer-max);
  visibility: visible;
  transition: max-height var(--dur-move) var(--ease-out),
              visibility 0s;
}

/* The scrolling part, inside the clip. Scrolling the drawer itself would move
   the rounded corner along with the content. */
.island-drawer-scroll {
  max-height: var(--island-drawer-max);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 0.65rem;
  scrollbar-width: thin;
  scrollbar-color: var(--depth-1) transparent;
}

/* The browser's own furniture, themed. A default scrollbar inside a plate
   like this one is the cheapest tell that the object was assembled rather
   than built. */
.island-drawer-scroll::-webkit-scrollbar { width: 10px; }
.island-drawer-scroll::-webkit-scrollbar-track { background: transparent; }
.island-drawer-scroll::-webkit-scrollbar-thumb {
  background: var(--depth-1);
  border: 3px solid var(--bg-secondary);
  border-radius: var(--radius-full);
}

/* Each panel rises as the drawer falls — the content is not sitting there
   waiting to be uncovered, it arrives with the box. One authored move, and
   the only one on this object besides the plate's own morph. */
.island-panel { display: none; }
.island-panel.is-open {
  display: block;
  animation: island-panel-rise var(--dur-move) var(--ease-out) both;
}

/* Out of the crease. The panel is hinged at the fold, so it swings down from
   it rather than sliding in from nowhere — a small rotation is enough to read
   as paper falling open, and any more of it reads as a card trick. */
@keyframes island-panel-rise {
  from { opacity: 0; transform: perspective(900px) rotateX(-9deg); }
  to   { opacity: 1; transform: none; }
}

.island-panel.is-open { transform-origin: top center; }

@media (prefers-reduced-motion: reduce) {
  .island-drawer { transition: none; }
  .island-panel.is-open { animation: none; }
}

/* ------------------------------------------------------------------- rows */

/* Every list in every drawer is this row. One row shape across the outline,
   the search results, the lesson switcher and the saved sections, because
   they are all the same gesture: a place, and pressing it goes there. */
.island-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}

.island-row {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  width: 100%;
  padding: 0.5rem 0.6rem;
  text-align: start;
  font-family: var(--font-body);
  font-size: 0.88rem;
  color: var(--text-primary);
  background: transparent;
  border: 2px solid transparent;
  /* Squared with the plate. Everything inside a title block is ruled to the
     same corner as the block itself; a stack of rounded rows inside a
     rectangle is the seam that says the panel was borrowed. */
  border-radius: var(--radius-xs);
  cursor: pointer;
  transition: background-color var(--transition-fast),
              border-color var(--transition-fast);
}

.island-row:hover,
.island-row:focus-visible {
  background: var(--bg-tertiary);
  border-color: var(--border-light);
  outline: none;
}

.island-row:focus-visible { border-color: var(--border-color); }

/* The row the keyboard is on. The list is driven by the arrow keys as much as
   by the pointer, so the cursor has to be a mark on the row and not only a
   focus ring the browser happens to draw. */
.island-row.is-cursor {
  background: var(--primary-light);
  border-color: var(--border-color);
}

/* Where you are standing now. */
.island-row[aria-current] {
  background: var(--primary-light);
  border-color: var(--border-color);
  font-weight: 700;
}

.island-row-mark {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  flex: none;
  min-width: 2.1ch;
  text-align: end;
  direction: ltr;
}

.island-row[aria-current] .island-row-mark { color: var(--depth-3); }

.island-row-body {
  display: flex;
  flex-direction: column;
  gap: 0.12rem;
  min-width: 0;
  flex: 1 1 auto;
}

.island-row-title {
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.island-row-note {
  font-size: 0.72rem;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A row you kept. Brass, and only ever brass — the second metal, spent on the
   one thing on this object that the student chose rather than arrived at. */
.island-row-keep {
  flex: none;
  color: var(--brass-edge);
  display: inline-flex;
}

.island-row-keep .lesson-icon { width: 15px; height: 15px; }

/* The depth of the row's own subject, when a list mixes lessons. */
.island-row-meter {
  flex: none;
  width: 42px;
  height: 6px;
  border: 1.5px solid var(--border-color);
  border-radius: 1px;
  background: var(--bg-tertiary);
  overflow: hidden;
}

.island-row-meter i {
  display: block;
  height: 100%;
  width: var(--row-progress, 0%);
  background: var(--depth-2);
}

/* Section headings inside a drawer: which list this is. Set in the soundings
   face and ruled off, the way the chart labels its own panels. */
.island-group {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.63rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding: 0.7rem 0.6rem 0.35rem;
}

.island-group::after {
  content: "";
  flex: 1 1 auto;
  height: 2px;
  background: var(--border-light);
}

.island-group:first-child { padding-block-start: 0.2rem; }

/* Nothing here. Says what would be here and how to put something in it —
   an empty list that only says "empty" has told the student nothing. */
.island-empty {
  padding: 1.1rem 0.7rem;
  text-align: center;
  font-family: var(--font-body);
  font-size: 0.82rem;
  color: var(--text-muted);
  line-height: 1.7;
}

.island-empty strong {
  display: block;
  font-size: 0.92rem;
  color: var(--text-secondary);
  margin-block-end: 0.25rem;
}

/* ------------------------------------------------------------- the search */

/* One field, and it is the drawer's own head rather than a control sitting in
   it — the search *is* that drawer, so it takes the top of it and the results
   fall out underneath. */
.island-search {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.55rem 0.7rem;
  border-block-end: 2px solid var(--border-light);
  background: var(--bg-tertiary);
}

.island-search .lesson-icon { flex: none; color: var(--text-muted); width: 19px; height: 19px; }

.island-search-input {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--font-body);
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text-primary);
  background: transparent;
  border: 0;
  outline: none;
  padding: 0.2rem 0;
}

.island-search-input::placeholder { color: var(--text-muted); font-weight: 500; }

/* Chrome draws its own clear button on type=search and it is not ours. */
.island-search-input::-webkit-search-decoration,
.island-search-input::-webkit-search-cancel-button { -webkit-appearance: none; }

/* The keyboard's own hint, in the soundings face. Only on a machine that has
   the key — a phone showing ⌘K is describing a keyboard nobody is holding. */
.island-key {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  font-weight: 700;
  color: var(--text-muted);
  background: var(--bg-secondary);
  border: 2px solid var(--border-light);
  border-radius: var(--radius-xs);
  padding: 0.1rem 0.3rem;
  flex: none;
  direction: ltr;
}

@media (hover: none) and (pointer: coarse) {
  .island-key { display: none; }
}

/* What matched, inside a row. Brass on the ground the row already has, never
   a fill that changes the row's own colour. */
.island-row mark {
  background: var(--brass-fill);
  color: var(--brass-on);
  border-radius: 2px;
  padding: 0 1px;
}

/* ------------------------------------------------------------- the slots */

/* Changing context is the island's one signature move, and it is a swap
   rather than a fade: what was there drops out under the plate's rule and
   what replaces it rises from the same edge, so the two are read as one
   object turning over rather than two states cross-dissolving.

   Driven by a class the controller sets for exactly one move; the plate's
   own width is already animating on --dur-morph underneath it. */
.island-slot-body > * {
  animation: island-slot-in var(--dur-move) var(--ease-out) both;
}

.island.is-turning .island-slot-body > * {
  animation: island-slot-out var(--dur-state) var(--ease-in) both;
}

@keyframes island-slot-in {
  from { opacity: 0; transform: translateY(9px); }
  to   { opacity: 1; transform: none; }
}

@keyframes island-slot-out {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateY(-9px); }
}

@media (prefers-reduced-motion: reduce) {
  .island-slot-body > *,
  .island.is-turning .island-slot-body > * { animation: none; }
}

/* The account block is not on this object any more — it moved to the index,
   at the head of the chart plate, where a student says who they are. Its
   styles moved with it, to css/catalog.css. */

/* One gesture, one button.

   The index button and the readout opened the same drawer on the same tab.
   Two controls for one gesture is one too many anywhere, and here the choice
   is easy: the figure is the one that also tells you something — 01/34 says
   which part you are standing in *and* is the way into the list, while
   «الفهرس» only said the second half of that. So inside a lesson the readout
   is the way in, and the button is only there on the screens with no readout
   to carry it. */
.island[data-context="reader"] #islandIndex {
  display: none !important;
}

/* ---------------------------------------------------------------- the dock */

/* The phone. The island leaves the top of the screen and docks to the bottom,
   because on a phone the top of the screen is the one place a thumb cannot
   reach and this object is meant to be reached constantly.

   It is the same object — same plate, same rule, same drawers — turned over:
   the drawer now falls *up*, out of the plate's top edge, so it still opens
   into the screen rather than off it. */
@media (max-width: 720px) {
  :root {
    /* The rail at the top is empty now, so it stops taking height. The hero
       reads this to pull itself up, and it must not pull up under a bar that
       is no longer there. */
    --header-h: env(safe-area-inset-top, 0px);
    --island-dock-h: calc(var(--island-h) + 1.1rem + env(safe-area-inset-bottom, 0px));
  }

  .main-header {
    position: fixed;
    /* Docked, the rail hugs whatever the plate is: it is out of the flow
       entirely, so there is no height for it to reserve and the plate grows
       upward on its own when a drawer opens. */
    height: auto;
    top: auto;
    inset-block-end: 0;
    inset-inline: 0;
    padding: 0 0 calc(0.55rem + env(safe-area-inset-bottom, 0px));
    /* Above the edge tab, which is also fixed and also 120 — at equal depth
       the document order decides, and the tab is written after the header, so
       it was landing on top of an open drawer. The island is the instrument
       the whole site is arranged around; nothing parked at the side of the
       screen goes over it. */
    z-index: 130;
    /* The rail no longer holds a place in the flow, so the page has to leave
       one: the body is padded by exactly the dock's height below. */
    pointer-events: none;
  }

  .main-header .island { pointer-events: auto; }

  /* The morph on scroll was the top bar's move and it has no meaning down
     here — a docked island is always an object. */
  .main-header.is-scrolled { padding-inline: 0; }

  body { padding-block-end: var(--island-dock-h); }

  /* Turned over, and it is one line because the plate is one box: reversing
     the column puts the drawer above the bar inside the same border, the same
     corner and the same shadow. The rail is fixed to the bottom of the screen
     and nothing constrains its height, so the plate grows upward on its own —
     no second set of corner rules, and no absolute positioning to undo. */
  .island {
    position: static;
    flex-direction: column-reverse;
  }

  .island,
  .island[data-drawer]:not([data-drawer=""]) {
    width: auto;
    max-width: none;
    margin-inline: 0.6rem;
  }

  .island-bar { padding-inline: 0.4rem; }

  /* The wordmark is the least useful thing on a phone's island and the first
     thing to go: the mark alone still says whose site this is, and the space
     it gives back is the space the tools need. */
  .island-brand-text { display: none; }

  .island-drawer-scroll { max-height: min(58vh, 26rem); }

  /* Fitting the plate to the phone, in the order things are worth keeping.

     A 360px screen leaves the bar about 300px once the plate's own margins
     and gutter are out, and the reader's full tool set does not fit in it:
     the sign-in's label alone is a third of the row, and the theme switch is
     a small painting rather than an icon. Every one of these is a decision
     about which control a thumb actually reaches for while reading, not a
     scale factor applied to all of them. */

  /* The sky, held to the row's height. It stays a painting — it is the one
     control on this plate anybody presses for pleasure — but it stops being
     the widest thing on the bar. */
  .island .theme-switch { width: 52px; height: 30px; }

  /* Last resort, and it must never be reached: if a context ever puts more on
     the bar than the screen can hold, the row gives up its gap before it
     gives up a control, and the plate still cannot spill past its own rule. */
  .island-bar { gap: 0.25rem; overflow: hidden; }
  .island-slot-tools { min-width: 0; }

  /* -------------------------------------------------- reading, on a phone

     The lesson's name lost. Six controls at `flex: none` took 273px of a
     365px bar and left the one thing that says what you are reading with 37 —
     "NumPy" rendered eleven pixels wide. Shaving each control would have
     bought back a few pixels of the same crush; what was actually wrong is
     that the desk's tool set was being carried onto a phone whole.

     So the bar is cut to what a thumb reaches for while reading, and the two
     that go are the two that are not reading:

     The index button, because the readout beside it opens the same drawer —
     one gesture had two buttons, and the figure is the one that also tells
     you something.

     That leaves the readout, the ribbon, the search, the print and the sky,
     and the name gets a width it can be read at. (The account used to be the
     other one cut here; it is off the island entirely now, on the index, and
     the index button is cut at every width — see the rule above the dock.) */

  /* And the name takes the room alone. The part you are standing in is named
     twice over already — as a figure on the readout, and in full at the top
     of the drawer that figure opens — so on a bar this narrow it is the
     lesson's name or nothing. */
  .island[data-context="reader"] .island-place-section {
    display: none;
  }

  /* Whatever is left, the name gets first claim on. Without this the two
     lines shrink in proportion to each other and both end up unreadable
     rather than one of them surviving. */
  .island-place-name { flex: 1 1 auto; }
}

/* A phone in landscape has no room for a docked bar and a page both. */
@media (max-height: 480px) and (orientation: landscape) {
  :root { --island-drawer-max: 60vh; }
}

/* ----------------------------------------------------------------- the tabs */

/* Three lists behind one button, because they are three answers to one
   question — "what else is there?" — and three buttons on the plate would be
   three ways of asking it. The strip is the drawer's own head, ruled off from
   the list the way the plate is ruled off from the page. */
.island-tabs {
  display: flex;
  gap: 0.3rem;
  padding: 0.5rem 0.6rem;
  border-block-end: 2px solid var(--border-light);
  background: var(--bg-tertiary);
}

.island-tab {
  flex: 1 1 auto;
  padding: 0.4rem 0.5rem;
  font-family: var(--font-display);
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--text-secondary);
  background: transparent;
  border: 2px solid transparent;
  border-radius: var(--radius-xs);
  cursor: pointer;
  transition: background-color var(--transition-fast),
              border-color var(--transition-fast),
              color var(--transition-fast);
}

.island-tab:hover { color: var(--text-primary); background: var(--bg-secondary); }

.island-tab:focus-visible {
  outline: 3px solid var(--accent-yellow);
  outline-offset: 1px;
}

/* The open list. Brass, like every other thing on this object that the
   student chose rather than arrived at. */
.island-tab[aria-selected="true"] {
  color: var(--brass-on);
  background: var(--brass-fill);
  border-color: var(--border-color);
  box-shadow: 2px 2px 0 var(--shadow-color);
}

/* ------------------------------------------------------- browser surfaces */

/* The parts nobody drew still carry the design. A selection highlight, a
   caret and a focus ring all ship with a colour that belongs to no design
   system, and inside an object this committed they are the seam that gives it
   away. */
.island ::selection {
  background: var(--brass-fill);
  color: var(--brass-on);
}

.island-search-input { caret-color: var(--primary); }

/* ------------------------------------------------------------- the filter */

/* A lesson filtered down to four of its forty-eight parts, with the control
   that did it living in another document, is the one way the split between
   the plate and the page could strand somebody. So a standing filter is
   always on the plate, and the chip is also how it is dropped.

   Brass, like everything else here the student chose. */
.island-filter {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  flex: none;
  max-width: 11rem;
  height: 26px;
  padding-inline: 0.5rem;
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--brass-on);
  background: var(--brass-fill);
  border: 2px solid var(--border-color);
  border-radius: var(--radius-xs);
  cursor: pointer;
  transition: filter var(--transition-fast);
}

.island-filter:hover { filter: brightness(0.94); }

.island-filter:focus-visible {
  outline: 3px solid var(--accent-blue);
  outline-offset: 2px;
}

.island-filter .lesson-icon { width: 13px; height: 13px; flex: none; }

.island-filter-text {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
