let _: Void = match (self.opname.as_str(), self.wrc) {
 
           ("flip", wrc@ WRC::UpdateSvg) => {
-            let nfaces = ipc.p.nfaces();
+            let nfaces = ipc.p.nfaces(y);
             let logents = log_did_to_piece(
               ioccults, &gs.occults, player, gpl, piece, gpc, ipc,
               "flipped"
 
       let mut updates = Vec::with_capacity(count_len);
       let mut pos = pos.unwrap_or(DEFAULT_POS_START);
       let mut z = gs.max_z.clone_mut();
+      let not_occ = ShowUnocculted::new_visible();
       for piece_i in count {
         let PieceSpecLoaded { p, occultable } = info.load(piece_i as usize)?;
         let ilks = &mut ig.ioccults.ilks;
           ilks.insert(ilkname, OccultIlkData { p_occ })
         });
         let face = face.unwrap_or_default();
-        if p.nfaces() <= face.into() {
+        if p.nfaces(not_occ) <= face.into() {
           throw!(SpecError::FaceNotFound);
         }
         let gpc = GPiece {
 
     fn want_several(&self) -> bool {
       self.size[0] < 20.0
     }
-    fn face_cols(&self) -> usize {
-      usize::from(self.p.nfaces())
+    fn face_cols(&self, unocc_ok: ShowUnocculted) -> usize {
+      usize::from(self.p.nfaces(unocc_ok))
         * if self.want_several() { SEVERAL } else { 1 }
     }
   }
   // clones as a bodge for https://github.com/rust-lang/rust/issues/34162
   pieces.sort_by_key(|p| (p.spec.item.clone(), p.spec.lib.clone()));
                      
-  let max_facecols = pieces.iter().map(|s| s.face_cols()).max().unwrap_or(1);
+  let max_facecols = pieces.iter().map(
+    |s| s.face_cols(unocc_ok)
+  ).max().unwrap_or(1);
   let max_uos = pieces.iter().map(|s| s.uos.len()).max().unwrap_or(0);
 
   println!("{}", &HTML_PRELUDE);
              Html::from_txt(&spec.item).0);
     println!(r#"<th align="left">{}</th>"#,
              p.describe_html(&GPiece::dummy())?.0);
-    let only1 = s.face_cols() == 1;
+    let only1 = s.face_cols(unocc_ok) == 1;
 
     for facecol in 0..(if only1 { 1 } else { max_facecols }) {
       let (face, inseveral) = if s.want_several() {
                _ => panic!(),
              });
       println!(r#">"#);
-      if face < (p.nfaces() as usize) {
+      if face < (p.nfaces(unocc_ok) as usize) {
         let viewport =
           [bbox[0].clone(), size.clone()]
           .iter().cloned()
 
   /// by convention, occult face is nfaces-1
   // xxx this is no good, we need a central definition of the occult
   // face to avoid weird behaviour with buggy gamespecs
-  fn nfaces(&self) -> RawFaceId;
+  fn nfaces(&self, y: ShowUnocculted) -> RawFaceId;
 
   #[throws(InternalError)]
   fn add_ui_operations(&self, _upd: &mut Vec<UoDescription>,
 
 
 #[typetag::serde]
 impl PieceTrait for Hand {
-  fn nfaces(&self) -> RawFaceId { 1 }
+  fn nfaces(&self, _: ShowUnocculted) -> RawFaceId { 1 }
   #[throws(IE)]
   fn svg_piece(&self, f: &mut Html, gpc: &GPiece,
                _vpid: VisiblePieceId, _: ShowUnocculted) {
 
         log_visible
       }
       OccK::Scrambled | OccK::Displaced{..} => {
-        let _face = ipc.p.nfaces() - 1; // xxx use other thing entirely
         let show = ipc.p.describe_html(gpc)?;
         call_log_callback(Some(&show))?
       },
 
     type WRC = WhatResponseToClientOp;
 
     let mut out = vec![];
-    if p.nfaces() > 1 {
+    if p.nfaces(y) > 1 {
       out.push(UoDescription {
         wrc: WRC::UpdateSvg,
         kind: UoKind::Global,
 
       else { format!("a {}", self.desc.0) }
     })
   }
-  fn nfaces(&self) -> RawFaceId { self.count_faces().try_into().unwrap() }
+  fn nfaces(&self, _: ShowUnocculted) -> RawFaceId {
+    self.count_faces().try_into().unwrap()
+  }
 
   fn itemname(&self, y: ShowUnocculted) -> &str { self.itemname(y) }
 }
 
 
 #[typetag::serde(name="Lib")]
 impl PieceTrait for Item {
-  fn nfaces(&self) -> RawFaceId { self.faces.len().try_into().unwrap() }
+  fn nfaces(&self, _: ShowUnocculted) -> RawFaceId {
+    self.faces.len().try_into().unwrap()
+  }
 
   #[throws(IE)]
   fn svg_piece(&self, f: &mut Html, gpc: &GPiece,